AI Memory for Microsoft Teams Conversations

Critical decisions happen in Teams channels and chats every day, then disappear into infinite scroll. This guide shows how to capture Microsoft Teams messages and meeting transcripts into REM Labs, creating a searchable AI memory layer over your team conversations.

The Architecture

Use the Microsoft Graph API to read channel messages and meeting transcripts, then push them to REM Labs where they become searchable with semantic retrieval. Your AI tools can then recall "What did engineering decide about the migration?" and get the actual conversation context.

Step 1: Set Up Microsoft Graph Access

Register an app in Azure AD with the required permissions:

npm install @remlabs/sdk @microsoft/microsoft-graph-client npm install @azure/identity
// Required Graph API permissions: // - ChannelMessage.Read.All (application) // - Chat.Read (delegated) // - OnlineMeetingTranscript.Read.All (application) import { Client } from "@microsoft/microsoft-graph-client"; import { ClientSecretCredential } from "@azure/identity"; import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials"; const credential = new ClientSecretCredential( process.env.AZURE_TENANT_ID, process.env.AZURE_CLIENT_ID, process.env.AZURE_CLIENT_SECRET ); const authProvider = new TokenCredentialAuthenticationProvider(credential, { scopes: ["https://graph.microsoft.com/.default"] }); const graph = Client.initWithMiddleware({ authProvider });

Step 2: Sync Channel Messages

import { RemClient } from "@remlabs/sdk"; const rem = new RemClient({ apiKey: process.env.REMLABS_API_KEY }); async function syncChannelMessages(teamId, channelId, channelName) { const messages = await graph .api(`/teams/${teamId}/channels/${channelId}/messages`) .top(50) .orderby("lastModifiedDateTime desc") .get(); for (const msg of messages.value) { if (!msg.body?.content) continue; // Strip HTML tags from Teams messages const text = msg.body.content.replace(/<[^>]*>/g, "").trim(); if (text.length < 10) continue; await rem.remember({ content: `[${channelName}] ${msg.from?.user?.displayName || "Unknown"}: ${text}`, namespace: "teams:channels", tags: [channelName, msg.from?.user?.displayName].filter(Boolean), metadata: { teams_message_id: msg.id, channel: channelName, author: msg.from?.user?.displayName, timestamp: msg.createdDateTime, source: "microsoft-teams" } }); } }

Step 3: Capture Meeting Transcripts

async function syncMeetingTranscripts() { const meetings = await graph .api("/me/onlineMeetings") .top(20) .orderby("startDateTime desc") .get(); for (const meeting of meetings.value) { const transcripts = await graph .api(`/me/onlineMeetings/${meeting.id}/transcripts`) .get(); for (const transcript of transcripts.value) { const content = await graph .api(`/me/onlineMeetings/${meeting.id}/transcripts/${transcript.id}/content`) .getStream(); await rem.remember({ content: `Meeting: ${meeting.subject}\n\n${content}`, namespace: "teams:meetings", tags: ["meeting", meeting.subject], metadata: { meeting_id: meeting.id, subject: meeting.subject, start: meeting.startDateTime, source: "teams-transcript" } }); } } }

Step 4: Query Team Knowledge

// Find decisions from channel conversations const decisions = await rem.recall({ query: "What was decided about the database migration timeline?", namespace: "teams:channels", limit: 10 }); decisions.forEach(m => { console.log(`[${m.metadata.channel}] ${m.metadata.author}:`); console.log(` ${m.content.slice(0, 150)}`); console.log(` -- ${m.metadata.timestamp}\n`); }); // Search meeting transcripts const meetingContext = await rem.recall({ query: "Q3 budget discussion", namespace: "teams:meetings", limit: 5 });

Webhook-Based Real-Time Sync

For real-time capture, use Graph change notifications to sync messages as they arrive:

// Create a subscription for new channel messages await graph.api("/subscriptions").post({ changeType: "created", notificationUrl: "https://your-server.com/webhooks/teams", resource: `/teams/${teamId}/channels/${channelId}/messages`, expirationDateTime: new Date(Date.now() + 3600000).toISOString() });

Compliance note: Ensure your Azure AD app has admin consent for the required permissions. Channel message access requires application-level permissions granted by a Teams admin. All data sent to REM Labs is encrypted at rest and in transit.

Give your team an AI memory

Free tier. Graph API integration. Semantic search on Teams conversations.

Get Started