Microsoft Teams Connector
Ingest Microsoft Teams channel messages, threaded replies, and 1:1/group chats into CortexDB.
Captures Teams channel and chat messages via the Microsoft Graph v1.0 API, polled incrementally on
lastModifiedDateTime.
Two ways to run this connector
Self-hosted — pip install 'cortexdb-connectors[teams]' then cortexdb-sync sync teams.
Managed — CortexDB runs the connector as a worker. See the Connectors overview.
Polling only — several capabilities are not ingested
Meeting transcripts, file/attachment contents, Graph change notifications (webhooks), and delta queries are not implemented as of 0.2.20. Messages are collected by polling only. See What is not ingested — it also determines which Graph permissions you need.
1. Prepare credentials in Microsoft Entra (Azure AD)
Azure portal → App registrations → New registration. Add Microsoft Graph application permissions:
| Permission | Why |
|---|---|
ChannelMessage.Read.All | Read channel messages and threaded replies |
Chat.Read.All | Read 1:1 and group chat messages (only if TEAMS_INCLUDE_CHATS=1) |
User.Read.All | Resolve message authors to names/emails |
TeamMember.Read.All | Resolve channel membership for visibility mapping |
Have an admin grant tenant-wide consent, then create a client secret and copy it.
Grant only these four permissions
Earlier revisions listed OnlineMeetingTranscript.Read.All — the connector never calls the transcript
API, so that consent is not required and should not be requested.
2. Configure (managed dashboard)
Settings → Connectors → Add Connector → Microsoft Teams. Paste the tenant ID, client ID, and client
secret; pick the teams to sync; set the scope template (default
org:<org>/source:teams/team:{team_id}/channel:{channel_id}); Start sync.
What gets written
Every episode's content is the message body, with HTML stripped to plain text.
| Event | Episode type | Notes |
|---|---|---|
| Channel message | message | Author resolved to display name + userPrincipalName |
| Reply in thread | message | parent_id and thread_id link it to the parent message |
| 1:1 / group chat message | message | Only when TEAMS_INCLUDE_CHATS=1 |
| Message in a meeting chat | meeting | The chat message, not a transcript |
| Message with an attachment | document | Tagged file_share with metadata.attachment_count; the file is not fetched |
@-mentions are recorded in metadata.mentions; high-importance messages are tagged
high_importance. The idempotency_key is the stable external id:
teams:channel:<team_id>:<channel_id>:<message_id>
teams:chat:<chat_id>:<message_id>The key is not version-aware
Because the key is stable rather than content-versioned, an edited message dedupes against its first ingest instead of being retained as a new version.
Visibility
| Source | Visibility |
|---|---|
| Standard (public) channel | organization |
| Private channel | restricted |
| 1:1 / group chat | private, with the chat's member ids as the allow-list |
What is not ingested
Genuine gaps in the shipped package, not configuration you're missing:
| Capability | Status in 0.2.20 |
|---|---|
| Meeting transcripts | Not implemented (meeting-chat messages are captured; the transcript is not) |
| File / attachment contents | Not implemented (typed document and counted, but no bytes uploaded) |
| Graph change notifications (webhooks) | Not implemented (no Teams webhook receiver) |
| Delta queries | Not implemented (uses the lastModifiedDateTime filter with @odata.nextLink paging) |
For attachment contents today, the Jira, Slack, and Freshdesk connectors ingest attachments through the blob pipeline.
Run it yourself
pip install 'cortexdb-connectors[teams]'
pip install cortexdb-cli && cortexdb init
export TEAMS_TENANT_ID=... # required
export TEAMS_CLIENT_ID=... # required
export TEAMS_CLIENT_SECRET=... # required
export TEAMS_TEAM_IDS=team-a,team-b # required for channel ingestion
export TEAMS_INCLUDE_CHATS=0 # 1 to also sync 1:1 / group chats
cortexdb-sync --api-url http://localhost:3141 sync teams
cortexdb-sync --api-url http://localhost:3141 watch teams --interval 60The connector honours Retry-After on Graph 429s (up to 5 retries), refreshes its token on a
mid-sync 401, and paces requests at 1 req/sec. The first run backfills backfill_days (default 30).