# teams-api AI-native Microsoft Teams integration — read conversations, send messages, and manage members via the Teams Chat Service REST API. Designed for autonomous AI agents that need to interact with Teams: read messages, reply to people, monitor conversations, and participate in team workflows. [Install in VS Code](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522teams%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522-p%2522%252C%2522teams-api%2540latest%2522%252C%2522teams-api-mcp%2522%255D%252C%2522env%2522%253A%257B%2522TEAMS_LOGIN%2522%253A%2522true%2522%257D%257D) [Install in VS Code Insiders](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522teams%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522-p%2522%252C%2522teams-api%2540latest%2522%252C%2522teams-api-mcp%2522%255D%252C%2522env%2522%253A%257B%2522TEAMS_LOGIN%2522%253A%2522true%2522%257D%257D) [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=teams&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi1wIiwidGVhbXMtYXBpQGxhdGVzdCIsInRlYW1zLWFwaS1tY3AiXSwiZW52Ijp7IlRFQU1TX0xPR0lOIjoidHJ1ZSJ9fQ%3D%3D) [![npm version](https://img.shields.io/npm/v/teams-api?style=flat-square)](https://www.npmjs.com/package/teams-api) [![MCP Registry](https://img.shields.io/badge/MCP_Registry-teams--api-green?style=flat-square)](https://registry.modelcontextprotocol.io) > [!NOTE] > This project was AI-generated using Claude Opus 4.6 with human guidance and review. ## Getting Started `teams-api` can be used in three ways: 1. **MCP server** for editors and AI tools — the recommended path for most users. 2. **CLI** for direct terminal use. 3. **Programmatic Node.js library** — advanced, documented near the end. ### Prerequisites - **[Node.js](https://nodejs.org/)** (v18 or later) — required for `npx`, which all MCP and CLI commands use. ### Install in your editor The quickest way to get started is to click one of the install badges above, or follow the instructions for your editor below. #### Editor-specific instructions
VS Code / VS Code Insiders **Option 1 — One-click install:** [Install in VS Code](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522teams%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522-p%2522%252C%2522teams-api%2540latest%2522%252C%2522teams-api-mcp%2522%255D%252C%2522env%2522%253A%257B%2522TEAMS_LOGIN%2522%253A%2522true%2522%257D%257D) [Install in VS Code Insiders](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522teams%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522-p%2522%252C%2522teams-api%2540latest%2522%252C%2522teams-api-mcp%2522%255D%252C%2522env%2522%253A%257B%2522TEAMS_LOGIN%2522%253A%2522true%2522%257D%257D) **Option 2 — CLI:** macOS / Linux: ```bash # VS Code code --add-mcp '{"name":"teams","command":"npx","args":["-y","-p","teams-api@latest","teams-api-mcp"],"env":{"TEAMS_LOGIN":"true"}}' # VS Code Insiders code-insiders --add-mcp '{"name":"teams","command":"npx","args":["-y","-p","teams-api@latest","teams-api-mcp"],"env":{"TEAMS_LOGIN":"true"}}' ``` Windows (PowerShell): ```powershell # VS Code code --add-mcp '{"name":"teams","command":"npx","args":["-y","-p","teams-api@latest","teams-api-mcp"],"env":{"TEAMS_LOGIN":"true"}}' # VS Code Insiders code-insiders --add-mcp '{"name":"teams","command":"npx","args":["-y","-p","teams-api@latest","teams-api-mcp"],"env":{"TEAMS_LOGIN":"true"}}' ``` Windows (CMD): ```cmd rem VS Code code --add-mcp "{\"name\":\"teams\",\"command\":\"npx\",\"args\":[\"-y\",\"-p\",\"teams-api@latest\",\"teams-api-mcp\"],\"env\":{\"TEAMS_LOGIN\":\"true\"}}" rem VS Code Insiders code-insiders --add-mcp "{\"name\":\"teams\",\"command\":\"npx\",\"args\":[\"-y\",\"-p\",\"teams-api@latest\",\"teams-api-mcp\"],\"env\":{\"TEAMS_LOGIN\":\"true\"}}" ``` **Option 3 — Manual config:** Add to your VS Code MCP config (`.vscode/mcp.json` or User Settings): ```json { "mcpServers": { "teams": { "command": "npx", "args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"], "env": { "TEAMS_LOGIN": "true" } } } } ```
Cursor [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=teams&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi1wIiwidGVhbXMtYXBpQGxhdGVzdCIsInRlYW1zLWFwaS1tY3AiXSwiZW52Ijp7IlRFQU1TX0xPR0lOIjoidHJ1ZSJ9fQ%3D%3D) Or add to `~/.cursor/mcp.json`: ```json { "mcpServers": { "teams": { "command": "npx", "args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"], "env": { "TEAMS_LOGIN": "true" } } } } ```
Claude Desktop Add to `claude_desktop_config.json` ([how to find it](https://modelcontextprotocol.io/quickstart/user)): ```json { "mcpServers": { "teams": { "command": "npx", "args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"], "env": { "TEAMS_LOGIN": "true" } } } } ```
Claude Code ```bash claude mcp add teams -- npx -y -p teams-api@latest teams-api-mcp ``` Then set the environment variable `TEAMS_LOGIN=true` in your shell before starting Claude Code. The server will ask for your email interactively on first use.
Windsurf Add to `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "teams": { "command": "npx", "args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"], "env": { "TEAMS_LOGIN": "true" } } } } ```
> [!TIP] > On macOS with a FIDO2 passkey, replace `TEAMS_LOGIN` with `TEAMS_AUTO` for fully unattended auth. See [Authentication](#authentication) for details. ### CLI You can also use the CLI directly without installing anything: ```bash npx -y -p teams-api@latest teams-api auth --login npx -y -p teams-api@latest teams-api list-conversations --login --limit 20 ``` If you use the CLI often, a global install is optional: ```bash npm install -g teams-api teams-api auth --login ``` ### Agent skill Compatible coding agents can install the optional [teams-api skill](skills/teams-api/) to learn when and how to use the CLI or SDK: ```bash npx -y skills add Maxim-Mazurok/teams-api --skill teams-api ``` The installer detects supported agents and asks whether to install the skill for the current project or globally. Once installed, the agent loads it automatically for relevant Teams tasks. ### Advanced Topics Manual token usage, debug-session auth, and programmatic Node.js usage are covered later in this README. ## Platform support | Feature | macOS | Windows / Linux | | ---------------------- | -------------- | --------------------------------- | | **Interactive login** | Full support | Full support | | **Auto-login (FIDO2)** | Full support | Not supported | | **Debug session** | Full support | Full support | | **Direct token** | Full support | Full support | | **Token caching** | macOS Keychain | Windows DPAPI / Linux secret-tool | | **CLI & MCP server** | Full support | Full support | | **Programmatic API** | Full support | Full support | > [!NOTE] > **Windows Defender false positive:** Older versions of this package used inline PowerShell to call the Windows DPAPI — a pattern that Windows Defender flags as ransomware-like behavior. This has been replaced with native Windows Credential Manager storage via [keytar](https://github.com/atom/node-keytar). If you hit issues on an older version, upgrade and re-run `teams-api auth --login`. See [SECURITY.md](./SECURITY.md) for details. ## Authentication Most users do not need to manage tokens manually. If you use interactive login, auto-login, or a Chrome debug session, `teams-api` captures the full token bundle automatically from Teams web traffic. That includes the base `skypeToken` plus the extra bearer tokens used for profile resolution and reliable people/chat/channel search. Those flows also detect the Teams chat region automatically from the intercepted request URLs, so most users do not need to set `--region` or `TEAMS_REGION`. On macOS, prefer auto-login when you have a platform authenticator / FIDO2 passkey set up. On other platforms, use interactive login. Direct token usage is the advanced/manual path. | Method | Description | Automation | Platform | | --------------------- | ---------------------------------------------------------------------------- | ---------------- | -------- | | **Auto-login** | Playwright launches system Chrome and captures the full token bundle | Fully unattended | macOS | | **Interactive login** | Opens a browser window and captures skype, middle-tier, and Substrate tokens | One-time manual | All | | **Debug session** | Connects to a running Chrome instance and captures the full token bundle | Semi-manual | All | | **Direct token** | Provide a previously captured token or token bundle explicitly | Manual | All | ### Auto-login (macOS only) Requires macOS with a platform authenticator (e.g. Intune Company Portal) and a FIDO2 passkey enrolled. Fully unattended — no browser window appears. ### Interactive login (recommended for Windows / Linux) The easiest cross-platform option. A browser window opens, you log in with any method your organization supports (password, MFA, passkey, etc.), and the token is captured automatically: ```bash teams-api auth --login ``` Optionally pre-fill your email: ```bash teams-api auth --login --email you@example.com ``` > [!NOTE] > Interactive login prefers an installed browser (Edge or Chrome) when available, and falls back to Playwright's bundled Chromium. ### Advanced / manual methods **Debug session** — start Chrome with `--remote-debugging-port=9222`, navigate to Teams and log in, then run: ```bash teams-api auth --debug-port 9222 ``` **Direct token** — advanced/manual only. Extract `x-skypetoken` from browser DevTools (Network tab) and pass it directly: ```bash teams-api list-conversations --token "" --region emea ``` If you want reliable people/chat/channel lookup and profile resolution on the direct-token path, also pass the extra bearer tokens captured from Teams requests: ```bash teams-api find-people \ --token "" \ --bearer-token "" \ --substrate-token "" \ --region emea \ --query "Jane Doe" ``` > [!TIP] > Skip this section if you are using `--login`, `--auto`, `TEAMS_LOGIN`, or `TEAMS_AUTO`. Those modes capture the full token bundle automatically. > [!TIP] > Direct-token mode still needs an explicit region. See [API regions](#api-regions) below. ## CLI Preferred without install: ```bash npx -y -p teams-api@latest teams-api [options] ``` Optional global install for frequent use: ```bash npm install -g teams-api teams-api [options] ``` The examples below use `teams-api` for readability. If you are not installing globally, replace it with `npx -y -p teams-api@latest teams-api`. ### Auth flags (available on all commands) | Flag | Description | | --------------------------- | -------------------------------------------------------------------------------- | | `--login` | Interactive browser login (all platforms) | | `--auto` | Auto-acquire token via FIDO2 passkey (macOS) | | `--email ` | Corporate email (required with `--auto`, optional otherwise) | | `--token ` | Use an existing skype token (advanced/manual) | | `--bearer-token ` | Optional middle-tier bearer token (advanced/manual) | | `--substrate-token ` | Optional Substrate bearer token (advanced/manual) | | `--debug-port ` | Chrome debug port (default: 9222) | | `--region ` | API region override. Auto-detected for login/debug auth; required with `--token` | | `--format ` | Output format: concise, detailed | | `--output ` | Export output to file (default format: concise) | ### Examples ```bash # Acquire a token (interactive — all platforms) teams-api auth --login # Acquire a token (auto — macOS with FIDO2) teams-api auth --auto --email you@example.com # List conversations teams-api list-conversations --login --limit 20 --format detailed # Find a conversation by topic teams-api find-conversation --auto --email you@example.com --query "Design Review" # Find a 1:1 chat by person name teams-api find-one-on-one --auto --email you@example.com --person-name "Jane Doe" # Find people, including office location when profile enrichment is available teams-api find-people --auto --email you@example.com --query "Jane Doe" # Get profiles for MRIs returned by people search or member listing teams-api get-profiles --auto --email you@example.com \ --user-identifiers "8:orgid:00000000-0000-0000-0000-000000000000" # Read messages (by topic name, person name, or direct ID) teams-api get-messages --auto --email you@example.com --chat "Design Review" teams-api get-messages --auto --email you@example.com --to "Jane Doe" --max-pages 5 teams-api get-messages --auto --email you@example.com --conversation-id "19:abc@thread.v2" --format detailed # Newest-first order (API returns newest-first; default is oldest-first/chronological) teams-api get-messages --auto --email you@example.com --chat "General" --order newest-first # Send a message teams-api send-message --auto --email you@example.com --to "Jane Doe" --content "Hello!" teams-api send-message --auto --email you@example.com --chat "Design Review" --content "Status update" # List members teams-api get-members --auto --email you@example.com --chat "Design Review" # Get current user info teams-api whoami --auto --email you@example.com # Export messages to a file (default format: concise) teams-api get-messages --auto --email you@example.com --chat "General" --output exports/general.md # Export as JSON to a file teams-api get-messages --auto --email you@example.com --chat "General" --format detailed --output exports/general.json ``` ## MCP server The MCP server exposes Teams operations as tools for AI agents via stdio transport. See [Getting Started](#install-in-your-editor) for editor-specific setup. ### Advanced: direct token configuration Use this only if you already have tokens from another flow or need to avoid browser-based auth entirely: ```json { "mcpServers": { "teams": { "command": "npx", "args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"], "env": { "TEAMS_TOKEN": "", "TEAMS_BEARER_TOKEN": "", "TEAMS_SUBSTRATE_TOKEN": "", "TEAMS_REGION": "emea" } } } } ``` > [!TIP] > If you do use direct tokens, `teams-api auth --login` prints the full token object as JSON. For basic chat operations, `skypeToken` is enough. For reliable people/chat/channel search and profile resolution, also pass `bearerToken` and `substrateToken`. ### Environment variables | Variable | Description | | ------------------------------- | -------------------------------------------------------------------------------------- | | `TEAMS_TOKEN` | Pre-existing skype token | | `TEAMS_BEARER_TOKEN` | Optional middle-tier bearer token | | `TEAMS_SUBSTRATE_TOKEN` | Optional Substrate bearer token | | `TEAMS_REGION` | API region override. Required with `TEAMS_TOKEN`; optional otherwise | | `TEAMS_EMAIL` | Corporate email. Optional — the server prompts the AI agent if needed | | `TEAMS_AUTO` | Set to `true` to enable auto-login (macOS + FIDO2) | | `TEAMS_LOGIN` | Set to `true` to enable interactive browser login | | `TEAMS_DEBUG_PORT` | Chrome debug port (default: 9222) | | `TEAMS_EDIT_REPLY_GUARD` | Edit reply guard: `allow` (default), `warn`, or `block`. See below | | `TEAMS_AGENT_MARKER` | Agent marker prefix for sent/edited messages (e.g. `Ⓜ`). See below | | `TEAMS_DELETE_MODE` | Delete mode: `hard` (default), `soft`, or `block`. See below | | `TEAMS_DELETE_TOMBSTONE` | Custom tombstone text for soft-delete mode. See below | | `TEAMS_AUDIT_LOG` | Audit logging: `off` (default), `stderr`, or `file:`. See below | | `TEAMS_PROTECTED_CONVERSATIONS` | Comma-separated glob patterns of conversations where edit/delete is blocked. See below | #### Agent marker When an AI agent sends or edits messages on behalf of a user, it can be hard to tell which messages were composed by the agent and which by the human. The `TEAMS_AGENT_MARKER` environment variable (or `--agent-marker` CLI flag / `agentMarker` MCP parameter) automatically prepends a configurable string to message content: ```jsonc { "env": { "TEAMS_AGENT_MARKER": "Ⓜ", // or "🤖", "[Bot]", etc. }, } ``` When set, every `send-message` and `edit-message` call prepends the marker followed by a space to the content. For example, with `TEAMS_AGENT_MARKER=Ⓜ`, sending "Hello world" produces "Ⓜ Hello world". The per-call parameter takes precedence over the environment variable. Pass an empty string to disable the marker for a specific call. #### Edit reply guard When editing a message that already has replies, the original context can be lost — replies may no longer make sense. The `TEAMS_EDIT_REPLY_GUARD` environment variable (or `--reply-guard` CLI flag / `replyGuard` MCP parameter) controls this: | Value | Behavior | | ------- | ---------------------------------------------------------------------------------- | | `allow` | Edit proceeds normally (default, backward-compatible) | | `warn` | Edit proceeds but an annotation is appended: _"⚠️ This message was edited after…"_ | | `block` | Edit is refused with an error listing the reply count | The per-call parameter takes precedence over the environment variable. #### Delete mode (soft-delete) Hard-deleting messages removes content permanently, which can be problematic for auditability and conversation flow in group chats. The `TEAMS_DELETE_MODE` environment variable (or `--delete-mode` CLI flag / `deleteMode` MCP parameter) controls how message deletion is handled: | Value | Behavior | | ------- | ------------------------------------------------------------------- | | `hard` | Permanently delete the message (default, current behavior) | | `soft` | Replace message content with a tombstone marker instead of deleting | | `block` | Refuse deletion entirely with an error | When using `soft` mode, the message content is replaced with `~~This message was removed by an agent~~` by default. Customize the tombstone text with `TEAMS_DELETE_TOMBSTONE` (or `--delete-tombstone` / `deleteTombstone`): ```jsonc { "env": { "TEAMS_DELETE_MODE": "soft", "TEAMS_DELETE_TOMBSTONE": "🗑️ [removed by automation]", }, } ``` The per-call parameters take precedence over environment variables. #### Audit logging State-modifying actions (edit and delete) can emit structured audit events for compliance and traceability. The `TEAMS_AUDIT_LOG` environment variable controls where events are written: | Value | Behavior | | ------------- | ----------------------------------------------------------- | | `off` | No audit logging (default) | | `stderr` | Write JSON Lines to stderr | | `file:` | Append JSON Lines to the specified file (created on demand) | ```jsonc { "env": { "TEAMS_AUDIT_LOG": "file:/var/log/teams-audit.jsonl", }, } ``` Each event is a single JSON line with the following fields: | Field | Description | | ------------------- | ----------------------------------------------------------------------------- | | `timestamp` | ISO 8601 timestamp | | `action` | `"edit"`, `"delete"`, or `"soft-delete"` | | `conversationId` | Conversation thread ID | | `conversationLabel` | Human-readable conversation label (topic or 1:1 partner name) | | `messageId` | Target message ID | | `content` | New content for edits, tombstone text for soft-delete, `null` for hard delete | Audit logging is designed to be silent — errors in the audit pipeline never affect tool execution. #### Protected conversations Some conversations contain sensitive or compliance-relevant information where accidental edits or deletions could be harmful. The `TEAMS_PROTECTED_CONVERSATIONS` environment variable (or `--protected-conversations` CLI flag / `protectedConversations` MCP parameter) blocks edit and delete actions in matching conversations: ```jsonc { "env": { "TEAMS_PROTECTED_CONVERSATIONS": "Incident *,*compliance*,Architecture Decisions", }, } ``` Patterns are comma-separated and support `*` as a wildcard (matches any characters). Matching is case-insensitive. When a conversation's name matches any pattern, both `edit-message` and `delete-message` throw an error before making any changes — the message is left untouched. The per-call parameter takes precedence over the environment variable, so individual tool invocations can override the configured patterns when needed. ### Available tools All MCP tools accept an optional `format` parameter (`concise` or `detailed`). Default format is `concise`. | Tool | Description | | -------------------------- | ----------------------------------------------------- | | `teams_list_conversations` | List available conversations | | `teams_find_conversation` | Find a conversation by topic or member name | | `teams_find_one_on_one` | Find a 1:1 chat with a person | | `teams_get_profiles` | Get user profiles, including office location | | `teams_find_people` | Search the organization directory | | `teams_find_chats` | Search chats by name or member | | `teams_get_messages` | Get messages from a conversation | | `teams_send_message` | Send a message to a conversation | | `teams_get_members` | List members of a conversation | | `teams_get_transcript` | Get a meeting transcript from a recorded conversation | | `teams_download_file` | Download message files and inline images | | `teams_describe_image` | Describe an inline Teams image with a vision model | | `teams_whoami` | Get the authenticated user's display name | Workflow guidance, tips, and important notes are served automatically via the MCP server's `instructions` field — no separate skill file needed. For the same content on the CLI, run `teams-api guide`. ### Image descriptions `teams_describe_image` reuses the existing Teams AMS image download support and sends the image bytes to an OpenAI-compatible vision endpoint. Configure it with `TEAMS_IMAGE_DESCRIPTION_API_KEY` or `OPENAI_API_KEY`. Optional overrides: `TEAMS_IMAGE_DESCRIPTION_MODEL` and `TEAMS_IMAGE_DESCRIPTION_BASE_URL`. Examples: ```bash teams-api describe-image --chat "Project Chat" --message-id 1773736076914 --image-index 0 teams-api describe-image --ams-object-id 0-eaua-d2-877b82634f4e978692f2243d445a6650 ``` ## API regions The Teams Chat Service URL varies by region. Login-based and debug-session auth detect it automatically. You only need to set `--region` or `TEAMS_REGION` when you are supplying tokens directly or want to force an override: | Region | Base URL | | ------ | -------------------------------------------- | | `apac` | `https://apac.ng.msg.teams.microsoft.com/v1` | | `emea` | `https://emea.ng.msg.teams.microsoft.com/v1` | | `amer` | `https://amer.ng.msg.teams.microsoft.com/v1` | ## Known limitations - **File attachments are not supported from remote AI hosts** (e.g. Claude.ai). The MCP server runs locally on your machine, so `--file` / `--image` paths must exist on your local filesystem. Cloud-hosted AI clients run in isolated containers and cannot access local paths. Use a local MCP client (Claude Desktop, Cursor, VS Code) or the CLI for file attachments. - Token lifetime is ~24 hours. After expiry, you must re-acquire. - The Teams Chat Service REST API is undocumented and may change without notice. - Auto-login requires macOS, system Chrome, a platform authenticator, and a FIDO2 passkey. On other platforms, use interactive login (`--login`) instead. - The members API returns empty display names for 1:1 chat participants. Use `findOneOnOneConversation()` to resolve names from message history. - Reaction actor identities come from the `emotions` field in message payloads. Parsing handles both JSON-string and array formats. ## Programmatic API This is the advanced integration path. Most users should start with MCP or CLI instead. Install the package in your project: ```bash npm install teams-api ``` Example: ```typescript import { TeamsClient } from "teams-api"; // Interactive login — opens a browser, you log in manually (all platforms) const client = await TeamsClient.fromInteractiveLogin(); // Or auto-login via platform authenticator (macOS + FIDO2 passkey) const autoClient = await TeamsClient.fromAutoLogin({ email: "you@example.com", }); // Advanced/manual: create a client from previously captured tokens const manualClient = TeamsClient.fromToken("skype-token-here", "emea"); const conversations = await client.listConversations(); const messages = await client.getMessages(conversations[0].id, { maxPages: 5, onProgress: (count) => console.log(`Fetched ${count} messages`), }); await client.sendMessage(conversations[0].id, "Hello from the API!"); const oneOnOne = await client.findOneOnOneConversation("Jane Doe"); const members = await client.getMembers(conversations[0].id); const profiles = await client.getProfiles(members.map((member) => member.id)); console.log(profiles.map((profile) => profile.userLocation)); ``` ### Conversation pagination `listConversations()` returns one page and enriches untitled chat names by default. SDK consumers can pass `{ enrichNames: false }` to avoid member and profile lookups. CLI and MCP behavior and options are unchanged. For complete, lightweight enumeration, use `listConversationsPage()`: ```typescript let backwardLink: string | undefined; do { const page = await client.listConversationsPage({ pageSize: 500, enrichNames: false, backwardLink, }); for (const conversation of page.conversations) { console.log( conversation.id, conversation.version, conversation.lastMessageTime, ); } backwardLink = page.backwardLink ?? undefined; } while (backwardLink); ``` Follow `backwardLink` until it is null, even when a page is short or becomes empty after system-stream filtering. Continuation URLs must use the current Chat Service origin. Name enrichment defaults to enabled on both SDK methods. Use `getMessages(id, { since })` to stop message pagination at a creation-time cutoff. This is not an edit/deletion delta feed; applications own checkpoint storage and reconciliation policy. ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, architecture, and implementation notes. ## License MIT