Integration
April 13, 2026
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