AI Agent for Meeting Notes: Auto-Capture Transcripts, Summaries & Action Items
You just walked out of a 45-minute meeting. There were decisions, action items, a vague promise about "circling back." Two days later, nobody can agree on what was actually decided. Sound familiar?
The average professional spends 31 hours per month in unproductive meetings. But meetings aren't the real problem — it's what happens after. Notes get lost. Action items evaporate. Context disappears.
What if an AI agent was always listening, always summarizing, and always following up — without you lifting a finger?
This guide shows you how to build exactly that. Not a fancy SaaS subscription — a system you own and control.
Why Existing Tools Aren't Enough
You've probably tried Otter.ai, Fireflies, or the built-in transcription in Zoom/Teams. They're decent at transcription. But here's where they fall short:
- Transcription ≠ understanding. A raw transcript is barely better than no notes — you still have to read the whole thing to find what matters.
- No follow-up. Action items get listed but nobody tracks them. They're just text in a document nobody revisits.
- No context. These tools don't know your projects, your team's shorthand, or what was decided last week. Every meeting starts from zero.
- Vendor lock-in. Your meeting data lives on someone else's server, in their format, behind their paywall.
An AI agent does what these tools can't: it understands what was discussed, connects it to previous decisions, and acts on what was agreed.
Architecture: The Three Layers
A meeting notes agent has three layers, just like any other AI agent:
🎙️ Layer 1: Capture Layer
Gets the raw audio/transcript into your system. Sources: Zoom webhooks, Google Meet recordings, Fireflies API, local Whisper transcription, or calendar-triggered recording bots.
🧠 Layer 2: Intelligence Layer
An LLM that processes transcripts into structured output: summary, decisions, action items (with owners and deadlines), open questions, and topic tags. This is where the magic happens.
📂 Layer 3: Memory & Action Layer
Stores processed notes, links them to projects, sends action items to your task tracker, and runs follow-up cron jobs to check completion.
Step 1: Set Up the Capture Pipeline
You have several options depending on your meeting platform. Here's the most common setup:
Option A: Fireflies API (Easiest)
If you're already using Fireflies, you can pull transcripts via their API:
import requests
def get_recent_transcripts(api_key, limit=10):
"""Fetch recent meeting transcripts from Fireflies."""
query = """
query {
transcripts(limit: %d) {
id
title
date
duration
sentences {
speaker_name
text
start_time
end_time
}
action_items
summary { overview keywords }
}
}
""" % limit
resp = requests.post(
"https://api.fireflies.ai/graphql",
json={"query": query},
headers={"Authorization": f"Bearer {api_key}"}
)
return resp.json()["data"]["transcripts"]
Option B: Local Whisper (Most Private)
Record meetings locally and transcribe with OpenAI's Whisper — completely offline, zero data sharing:
# Install Whisper
pip install openai-whisper
# Transcribe a meeting recording
whisper meeting-2026-02-26.mp3 \
--model medium \
--language en \
--output_format json \
--output_dir ./transcripts/
# For speaker diarization (who said what)
pip install pyannote.audio
# Combine Whisper + pyannote for speaker-labeled transcripts
Option C: Zoom/Teams Webhooks (Real-time)
Both Zoom and Teams can send webhook events when recordings are ready:
// Express webhook handler for Zoom
app.post('/webhook/zoom', async (req, res) => {
const { event, payload } = req.body;
if (event === 'recording.completed') {
const { download_url, topic, start_time } = payload;
// Download recording
const audio = await downloadRecording(download_url);
// Transcribe
const transcript = await transcribe(audio);
// Process with AI agent
await processMeetingNotes({
title: topic,
date: start_time,
transcript
});
}
res.sendStatus(200);
});
💡 Pro Tip: Calendar-Triggered Recording
Set up a cron job that checks your calendar, auto-joins meetings via a bot account, and records them. Tools like recall.ai provide meeting bot APIs, or you can build your own with headless Chrome + audio capture.
Step 2: Build the Intelligence Layer
This is where a raw transcript becomes structured, actionable intelligence. The key is a well-crafted system prompt:
MEETING_NOTES_PROMPT = """
You are a meeting intelligence agent. Process this transcript
and extract:
1. **Summary** (3-5 sentences, executive-level)
2. **Key Decisions** (what was decided, by whom)
3. **Action Items** (task, owner, deadline if mentioned)
4. **Open Questions** (unresolved items needing follow-up)
5. **Topic Tags** (for searchability)
6. **Sentiment** (overall tone: productive/tense/brainstorm/etc.)
RULES:
- Be specific. "Follow up on the proposal" is useless.
"Sarah sends revised budget to Tom by Friday" is useful.
- If a deadline wasn't mentioned, flag it as "⚠️ No deadline set"
- If ownership is unclear, flag it as "⚠️ Owner unclear"
- Connect to previous meetings if context is provided
Output as structured JSON.
"""
import json
from openai import OpenAI
def process_transcript(transcript, previous_context=""):
client = OpenAI()
messages = [
{"role": "system", "content": MEETING_NOTES_PROMPT},
]
if previous_context:
messages.append({
"role": "user",
"content": f"Context from recent meetings:\n{previous_context}"
})
messages.append({
"role": "user",
"content": f"Process this transcript:\n\n{transcript}"
})
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
Sample Output
{
"summary": "Q1 marketing review. Team decided to shift 40% of budget
from paid ads to content marketing based on CAC data. New landing
page needed by March 15. Sarah's team will lead, Tom provides copy.",
"decisions": [
{
"decision": "Shift 40% of Q2 marketing budget from paid ads to content",
"made_by": "Lisa (VP Marketing)",
"rationale": "Paid CAC increased 60% YoY, content CAC decreased 25%"
}
],
"action_items": [
{
"task": "Create new landing page for content marketing funnel",
"owner": "Sarah",
"deadline": "2026-03-15",
"priority": "high"
},
{
"task": "Write copy for 3 pillar blog posts",
"owner": "Tom",
"deadline": "⚠️ No deadline set",
"priority": "medium"
},
{
"task": "Pull Q1 CAC comparison data for board deck",
"owner": "⚠️ Owner unclear",
"deadline": "2026-03-01",
"priority": "high"
}
],
"open_questions": [
"Which content topics to prioritize? Waiting on SEO audit results.",
"Do we need additional headcount for content production?"
],
"tags": ["marketing", "budget", "q1-review", "content-strategy"],
"sentiment": "productive"
}
Step 3: Connect to Your Task Tracker
Action items are worthless if they stay in a document. Push them where work actually happens:
# Push action items to Linear, Notion, Todoist, etc.
def push_to_linear(action_items, team_id):
"""Create Linear issues from meeting action items."""
for item in action_items:
linear.create_issue(
team_id=team_id,
title=item["task"],
assignee=resolve_user(item["owner"]),
due_date=item.get("deadline"),
priority=map_priority(item["priority"]),
labels=["meeting-action-item"],
description=f"Source: {meeting_title}\nDate: {meeting_date}"
)
def push_to_notion(action_items, database_id):
"""Add action items to a Notion database."""
for item in action_items:
notion.pages.create(
parent={"database_id": database_id},
properties={
"Task": {"title": [{"text": {"content": item["task"]}}]},
"Owner": {"people": [resolve_notion_user(item["owner"])]},
"Due": {"date": {"start": item.get("deadline")}},
"Status": {"select": {"name": "To Do"}},
"Source": {"rich_text": [{"text": {"content": meeting_title}}]}
}
)
Step 4: Add Memory (The Game-Changer)
This is what separates a meeting agent from a glorified transcription tool. With memory, your agent knows:
- What was decided in previous meetings on the same topic
- Which action items from last week are still open
- Who tends to take on which types of tasks
- Recurring themes and patterns across meetings
# Simple file-based meeting memory
import os
import json
from datetime import datetime, timedelta
MEMORY_DIR = "./meeting-memory"
def store_meeting(meeting_data):
"""Save processed meeting to memory."""
date = meeting_data["date"]
filepath = f"{MEMORY_DIR}/{date}-{slugify(meeting_data['title'])}.json"
os.makedirs(MEMORY_DIR, exist_ok=True)
with open(filepath, "w") as f:
json.dump(meeting_data, f, indent=2)
def get_recent_context(days=14, tags=None):
"""Pull context from recent meetings for the intelligence layer."""
cutoff = datetime.now() - timedelta(days=days)
context_parts = []
for filename in sorted(os.listdir(MEMORY_DIR), reverse=True):
with open(f"{MEMORY_DIR}/{filename}") as f:
meeting = json.load(f)
meeting_date = datetime.fromisoformat(meeting["date"])
if meeting_date < cutoff:
break
if tags and not set(tags) & set(meeting.get("tags", [])):
continue
# Include summary + open action items
open_items = [
i for i in meeting["action_items"]
if i.get("status") != "completed"
]
context_parts.append(
f"[{meeting['date']}] {meeting['title']}\n"
f"Summary: {meeting['summary']}\n"
f"Open items: {json.dumps(open_items)}\n"
)
return "\n---\n".join(context_parts)
🚀 Want the Complete Meeting Agent Setup?
The AI Employee Playbook includes our production meeting agent config — including the 3-File Framework, memory templates, and cron job recipes.
Get the Playbook — €29Step 5: Automate Follow-Up
The most powerful feature: your agent checks on commitments so you don't have to.
# Daily follow-up cron job
def daily_followup():
"""Check for overdue and upcoming action items."""
today = datetime.now().date()
overdue = []
upcoming = []
for meeting_file in os.listdir(MEMORY_DIR):
with open(f"{MEMORY_DIR}/{meeting_file}") as f:
meeting = json.load(f)
for item in meeting["action_items"]:
if item.get("status") == "completed":
continue
deadline = item.get("deadline")
if not deadline or deadline.startswith("⚠️"):
continue
due = datetime.fromisoformat(deadline).date()
if due < today:
overdue.append({**item, "meeting": meeting["title"]})
elif due <= today + timedelta(days=2):
upcoming.append({**item, "meeting": meeting["title"]})
# Send digest via Slack/email/Telegram
if overdue or upcoming:
send_digest(overdue, upcoming)
def send_digest(overdue, upcoming):
"""Send action item digest to team channel."""
msg = "📋 **Meeting Action Items Update**\n\n"
if overdue:
msg += "🔴 **Overdue:**\n"
for item in overdue:
msg += f"- {item['task']} ({item['owner']}) — due {item['deadline']}\n"
msg += f" _from: {item['meeting']}_\n"
if upcoming:
msg += "\n🟡 **Due Soon:**\n"
for item in upcoming:
msg += f"- {item['task']} ({item['owner']}) — due {item['deadline']}\n"
send_to_slack(msg, channel="#team-updates")
The Complete Pipeline
Here's how all the pieces fit together in a single orchestration loop:
# Main meeting agent loop
async def meeting_agent_loop():
"""Run on cron: checks for new recordings, processes them."""
# 1. Check for new transcripts
new_meetings = await get_new_transcripts()
for meeting in new_meetings:
# 2. Get context from recent related meetings
context = get_recent_context(
days=14,
tags=meeting.get("predicted_tags")
)
# 3. Process with LLM
result = process_transcript(
meeting["transcript"],
previous_context=context
)
# 4. Store in memory
store_meeting({
**result,
"title": meeting["title"],
"date": meeting["date"],
"participants": meeting["participants"],
"raw_transcript": meeting["transcript"]
})
# 5. Push action items to task tracker
if result["action_items"]:
push_to_linear(result["action_items"], TEAM_ID)
# 6. Send summary to team
send_meeting_summary(result, channel="#meeting-notes")
# 7. Flag items needing attention
unclear = [
i for i in result["action_items"]
if "⚠️" in str(i.get("owner", "")) or
"⚠️" in str(i.get("deadline", ""))
]
if unclear:
send_clarification_request(unclear, meeting["participants"])
Cost Breakdown
| Component | Tool | Monthly Cost |
|---|---|---|
| Transcription | Whisper (local) / Fireflies Free | $0 |
| LLM Processing | GPT-4o / Claude | $5-15 |
| Storage | Local files / S3 | $0-2 |
| Task Tracker Push | Linear/Notion API | $0 (included) |
| Notifications | Slack/Telegram webhook | $0 |
| Total | $5-17/mo |
Compare this to dedicated meeting intelligence tools at $15-40/user/month. For a 10-person team, that's $150-400/mo vs your $15.
5 Mistakes to Avoid
1. Summarizing Too Aggressively
A two-sentence summary of a complex strategy meeting is useless. Let the LLM produce a thorough summary, then offer a one-liner TL;DR separately. You can always skim — you can't un-lose context.
2. Not Resolving "Owner Unclear" Items
If your agent flags unclear ownership, that's not a bug — it's a feature. But you need to act on it. Build a workflow that pings the meeting organizer to clarify ownership within 24 hours.
3. Ignoring Speaker Attribution
Knowing who said what matters enormously for accountability. Invest in speaker diarization (Whisper + pyannote, or use a service that does it). "Someone said we should do X" is worthless. "Lisa said she'd handle X" is gold.
4. Processing Meetings in Isolation
Without memory, every meeting is a fresh start. The agent won't notice that the same action item has been carried over for three weeks, or that two teams are making contradictory decisions. Always feed in recent context.
5. Over-Engineering Day One
Start with: transcribe → summarize → post to Slack. That alone saves hours. Add action item tracking in week 2, memory in week 3, follow-up automation in week 4. Ship value early.
Privacy & Security
🔒 Important Considerations
- Consent: Always inform participants that meetings are being recorded and processed. Check local laws — many jurisdictions require explicit consent.
- Data residency: Use local Whisper for sensitive meetings. Don't send confidential board discussions through third-party APIs.
- Access control: Not everyone should see every meeting's notes. Implement role-based access or channel-specific routing.
- Retention: Set automatic deletion policies. You probably don't need transcripts from 18 months ago.
Real-World Example
"We run 40+ meetings a week across 4 teams. Before the agent, action items had a 35% completion rate — people just forgot. After deploying the meeting agent with daily follow-up digests, completion jumped to 82%. The agent paid for itself in the first week."
— Engineering Director, 50-person SaaS company
What's Next
Once your basic meeting agent is running, the extensions are endless:
- Meeting prep briefs: Before each meeting, the agent sends you a one-pager with open items from the last meeting, relevant context, and suggested agenda items.
- Pattern detection: "This topic has come up in 4 meetings without resolution. Escalate?"
- Meeting scoring: Track which meetings produce the most action items and decisions. Kill the ones that don't.
- Cross-meeting insights: "Marketing and Product are both planning Q2 campaigns that might conflict. Heads up?"
The meeting agent is one of the highest-ROI agents you can build. It touches every team, saves real hours, and surfaces information that would otherwise get lost. Start with transcription + summaries, and let it grow from there.
⚡ Build Your First AI Agent This Weekend
The AI Employee Playbook gives you everything: the 3-File Framework, 10+ agent recipes (including meeting notes), deployment guides, and cost optimization strategies.
Get the Playbook — €29