Teams messages mix action items, status updates, jokes, and "LGTM" replies. Treating every message as a task source creates garbage. Ignoring Teams creates the same leak as email — commitments that never reach Jira or a task list.
Our Teams extraction pipeline scans recent messages, strips HTML noise in code, batches the remainder to the LLM, and persists tasks with stable source IDs. The design bias is false negatives over invented work.
Chat is not a task queue
Unlike email, chat lacks subject lines and explicit threading hierarchy. Context lives in replies, @mentions, and channel names. Extraction needs message metadata — team, channel, author, timestamp — in the prompt so the model knows who owes whom what.
Hygiene in code, judgment in the model
prepareTeamsMessage strips HTML and drops empty or ultra-short bodies (under three characters). We deliberately do not regex-classify "LGTM" vs "please review" in TypeScript — that becomes a maintenance nightmare. The LLM decides actionability; code only removes unusable input.
Pipeline stages
1. Filter. Max 40 messages, 14-day lookback. Older chat is stale for task capture.
2. Prepare. Strip HTML, collect skip reasons for stats (empty, too_short).
3. Batch LLM. Chunks of eight messages per call. Include user profile (name, email) so the model knows who "you" is versus assignees.
4. Persist. Create tasks with source: TEAMS, sourceId from message identity, sourceData with confidence and extraction method.
const stats = {
messagesScanned, messagesSkipped, messagesActionable,
tasksExtracted, tasksCreated, duplicatesSkipped,
skippedReasons: { empty: 2, too_short: 5 },
llmBatches: 4,
}Expose stats in the API response so the UI can toast "3 tasks from 12 messages, 8 skipped as noise."
Dedupe by message ID
Teams dedupe checks userId + source TEAMS + sourceId only — one task row per message ID on retry. Unlike email, we do not also match title; one message should not spawn duplicate rows on pipeline re-run.
Teams vs email extraction
Email uses temp IDs in batch prompts and title-based dedupe within the same message. Teams uses native message IDs directly. Email defers sync cursors; Teams ingestion is separate from mail sync entirely.
Both require AI consent and rate limiting. Both link tasks back to source context — Teams stores team/channel in sourceData for display even when deep links are imperfect.
Related: email extraction pipeline · inbox task capture. Join the Kvika beta.