Home / Blog / Automate CRM Updates from Teams Transcripts

How to Automate CRM Updates from Microsoft Teams Meeting Transcripts

Teams records the meeting, the transcript sits in SharePoint, and nothing happens next. Your reps leave the call, go back to their queue, and the CRM stays exactly as it was before the conversation happened.

This post walks through the architecture to fix that: a pipeline that detects a completed meeting, pulls the transcript, extracts structured data with an LLM, and writes it to the right CRM fields automatically.

Who this is for: RevOps engineers, sales ops architects, and IT automation leads who are comfortable with API work and want a concrete technical picture before scoping the build.


What You're Building

The pipeline has four stages:

  1. Detect a completed Teams meeting and retrieve its transcript via Microsoft Graph
  2. Send the transcript text to an LLM with a structured extraction prompt
  3. Map the extracted fields to your CRM schema
  4. Write the data back via CRM API and notify the rep

Each stage has its own failure modes and design decisions. Here's what each one looks like in practice.


1
Get the Transcript

Teams stores transcripts as VTT or JSON files attached to the online meeting record. You access them through the Microsoft Graph Calling API.

The endpoint to list transcripts for a meeting:

GET /me/onlineMeetings/{meetingId}/transcripts

To fetch the actual content of a specific transcript:

GET /me/onlineMeetings/{meetingId}/transcripts/{transcriptId}/content
    ?$format=text/vtt

You can also request application/json instead of VTT if you prefer a structured format directly.

Required Graph permission: OnlineMeetingTranscript.Read.All (application permission, requires admin consent). If you're building this as a delegated flow where the rep triggers it themselves, OnlineMeetingTranscript.Read works, but application-level is more practical for automated pipelines.

Triggering the pipeline: You have two options. Poll on a schedule using the list endpoint, or subscribe to change notifications on the communications/onlineMeetings resource. The change notification path is lower latency and avoids unnecessary API calls, but it requires a publicly reachable webhook endpoint and a notification subscription with a short expiry you'll need to renew.

One thing to account for: transcripts are not immediately available when a meeting ends. There's a processing delay, typically a few minutes. Build a retry loop with exponential backoff rather than a single fetch at meeting-end.

2
Extract Structured Data

Once you have the transcript text, send it to an LLM with a prompt that defines exactly what you want back. Claude and GPT-4o both work well here. The key is being specific about your output schema.

A minimal extraction prompt looks like this:

You are a sales call analyst. Given the meeting transcript below,
extract the following fields and return them as valid JSON.

Fields to extract:
- next_steps: array of objects with { action, owner, due_date }
- commitments: array of objects with { commitment, owner, due_date }
- competitors_mentioned: array of strings
- budget_signals: string or null (direct quotes only, no inference)
- decision_timeline: string or null
- contact_names: array of objects with { name, title }
- confidence: number from 0 to 1 (your confidence in the extraction)

If a field is not present in the transcript, return null or an empty array.
Do not infer information that was not stated.

Transcript:
---
{transcript_text}
---

A few design choices worth making explicit:

  • Ask for a confidence score. You'll use it downstream to route low-confidence extractions to a human review queue instead of writing them directly to CRM.
  • Distinguish signal from inference. For budget signals especially, you want direct quotes, not the LLM's interpretation. Prompt explicitly for this.
  • Set a token budget. Long transcripts can easily exceed context limits. If your calls routinely run over 90 minutes, consider chunking the transcript and merging results, or using a model with a longer context window.

Run the extraction synchronously if your pipeline can tolerate a 2-5 second API call, or queue it asynchronously with a job ID you can poll. For high-volume teams, async is the right default.

3
Map to CRM Fields

The extracted JSON needs to land in the right places in your CRM. The mapping depends on your schema, but here are the common targets:

Extracted Field Dynamics 365 Target Salesforce Target
next_steps Task records linked to opportunity Task / Activity records
commitments Forecast comments, notes field Next Steps field, Notes
competitors_mentioned competitorid lookup or custom field Competitor field on opportunity
budget_signals Custom field or notes Custom field or notes
decision_timeline estimatedclosedate (with approval) CloseDate (with approval)
contact_names Contact roles on opportunity OpportunityContactRole records

A note on close dates: don't overwrite estimatedclosedate or CloseDate automatically. Surface the extracted timeline to the rep and let them accept or edit it. Auto-updating close dates from transcripts will erode trust in the system fast.

For contact names, cross-reference against existing contacts in CRM before creating new records. Match on name and company. If confidence is below your threshold, flag for human review rather than creating a duplicate contact.

4
Write Back via CRM API

Dynamics 365: Use OData PATCH to update an existing opportunity record:

PATCH /api/data/v9.2/opportunities({opportunityid})
Content-Type: application/json

{
  "msp_forecastcomments": "Next call scheduled for June 12. Budget confirmed at $80K range.",
  "competitorid": "/competitors({competitorid})"
}

To create a follow-up task linked to the opportunity:

POST /api/data/v9.2/tasks
Content-Type: application/json

{
  "subject": "Send proposal draft",
  "scheduledend": "2026-06-12T17:00:00Z",
  "regardingobjectid_opportunity@odata.bind": "/opportunities({opportunityid})"
}

Salesforce: Use the REST API with a PATCH to the opportunity endpoint, or composite requests if you're updating multiple objects at once:

PATCH /services/data/v60.0/sobjects/Opportunity/{opportunityId}
Content-Type: application/json

{
  "Description": "Budget confirmed. Decision by end of Q2.",
  "NextStep": "Send proposal by June 12"
}

Error handling and confidence thresholds: Set a confidence cutoff (0.75 is a reasonable starting point). Extractions above the threshold write directly to CRM. Below the threshold, write to a staging table or queue and send the rep a review card instead. Log every write attempt with the source transcript ID, the extracted values, the confidence score, and the outcome. You'll need that audit trail when a rep disputes a field value.

Rate limits are a real concern at scale. Dynamics 365 enforces API limits per service principal. Salesforce has per-org daily limits. Batch your writes where possible and implement a retry queue with jitter.

5
Notify the Rep

The automation is only useful if the rep knows what happened and can correct it when it's wrong. Send a summary after each write.

Two good options: a Teams Adaptive Card sent to the rep's 1:1 chat with the bot, or an email summary. The Adaptive Card is better because it can include inline edit buttons.

A minimal card shows: what was written, which fields were updated, the confidence score, and a link to the CRM record. If anything was routed to the review queue, surface those items with an "Accept" or "Edit" action the rep can take without leaving Teams.

The notification also serves as an implicit training signal. If reps are consistently correcting the same field, that's a sign your extraction prompt needs work, or that field isn't worth automating.


What Makes This Harder Than It Looks

Matching the meeting to the right CRM record

This is the hardest part of the whole pipeline. Teams doesn't know which opportunity a meeting belongs to. You have a few options: require the rep to include an opportunity ID or CRM link in the meeting subject or description (works, but adds friction), match on attendee emails against CRM contact records (fragile, breaks with external attendees on different domains), or use a scheduling integration that stamps the meeting with metadata at booking time.

The most reliable approach is a hybrid: try automated matching on email, fall back to a disambiguation card when confidence is low, and let the rep confirm before you write anything.

Speaker attribution in multi-party calls

VTT transcripts include speaker labels, but they're often generic identifiers rather than display names until Teams resolves them. On external calls with customers, you may see labels like Speaker 1 and Speaker 2 rather than real names.

Pass the attendee list from the Graph meeting record alongside the transcript so the LLM can attempt to correlate speakers with known participants. It's not perfect, but it catches most cases on two-party calls. For larger calls, treat speaker attribution as best-effort and flag it in the output.

VTT format and timestamp noise

Raw VTT files include timecodes and segment breaks that add token overhead and can confuse extraction. Strip the timecodes before sending to the LLM, or convert to a clean speaker-labeled format first:

00:01:23.450 --> 00:01:27.800
[Speaker 1]: We're planning to make a decision by end of June.

Becomes:

Speaker 1: We're planning to make a decision by end of June.

A simple regex pass over the VTT cleans this up in a few lines of code. It meaningfully reduces token count on long transcripts.


The Shortcut

This pipeline works. Teams have shipped it. But building it end-to-end takes real time: Graph API setup and permission consent, LLM prompt iteration, CRM field mapping with your specific schema, error handling, the rep notification layer, and the ongoing maintenance as your CRM schema and Teams API behavior evolve.

Resurg builds this as a fixed-scope project. You get the full pipeline scoped to your CRM, your fields, and your team's workflow, without the six-week integration slog.

Skip the integration slog.

Resurg delivers this pipeline as a fixed-scope AI Revenue Build: scoped, priced, and shipped. Book a 30-minute discovery call to see if it fits.

Book a free discovery call →