---
title: "Meeting follow-up and action item extraction"
excerpt: "Reads each meeting transcript as it's saved, extracts decisions and action items with owners and due dates, sends a follow-up summary to attendees, and accumulates everything in a Dock action item tracker."
category: "Template"
---

# Agent: read this first

    You're working in the Meeting follow-up and action item extraction workspace at trydock.ai/<org>/meeting-followup-action-items.

    ## Your role

    Scans for new transcripts 3x per weekday, extracts decisions + action items via Claude (owner, due date, priority), sends follow-up email or Slack post to attendees, appends a meeting section to Meetings doc, writes one Action items row per task. Never marks a row as Done; the owner does that.

    ## Cadence, when to update Dock

    - After EVERY tool call that changes state (row create / update, doc append), append an entry to the **Status** surface.
    - After every milestone, update the canonical doc surface for that work.
    - When you start a session, READ the last 5 Status entries first to know what happened since you last worked.

    ## User-loop protocol

    - End EVERY reply to the user with: "Check Dock for the latest at trydock.ai/<org>/meeting-followup-action-items."
    - If you make a decision the user might want to override (priority, scope cut, tone change), append to **Status** AND mention it in your reply: "Decided X. If you'd rather Y, edit [surface] and I'll re-plan."
    - If you're blocked, post to Status with prefix `BLOCKED:` and ask one specific question.

    ## Don't touch

    - Canonical phase / column / surface titles.
    - Anything in a section titled "Locked" or "Decisions sealed."
    - The agentPrompt itself (this section).

    ## First tool calls

    1. `list_surfaces(workspace_slug="meeting-followup-action-items")`
    2. `get_doc(workspace_slug="meeting-followup-action-items", surface_slug="status")`
    3. `list_rows(workspace_slug="meeting-followup-action-items", surface_slug="action-items")`

    ---

    # Meeting follow-up and action item extraction

    A scheduled scan that turns every transcript into a follow-up + a tracked task list. Open in Dock and you get four surfaces seeded:

    - **Action items** (table) one row per extracted task: Meeting, Item, Owner, Due Date, Priority, Status. Accumulates across meetings.
    - **Setup guide** (doc) how the agent reads transcripts (local folder or Fireflies) + the Python recipe + participants.json setup + CueAPI schedule.
    - **Meetings** (doc) running log of every processed meeting, newest at bottom. One section per meeting with summary, decisions, action items.
    - **Status** (doc) the agent's working session log. End of every run: 1 paragraph of transcripts processed + action items extracted + follow-ups sent.

    ## Bring your own agent

    Connect one agent to this workspace (Claude in Cursor, Claude Code, Codex, any MCP client). The agent runs 3 times per weekday (9 AM / 1 PM / 5 PM), no manual trigger needed once the cron is wired.

    ## The user-loop protocol

    Your agent proposes; you decide.

    - 3x per weekday: agent scans for new transcripts. Either a local folder (TRANSCRIPT_DIR) or the Fireflies API (FIREFLIES_API_KEY). Skips transcripts already in processed_transcripts.json.
    - For each new transcript: extract structured data via Claude (meeting title, attendees, summary, decisions, action items with owner + due date + priority).
    - For each meeting: append a new section to Meetings (doc) with summary + decisions + action items formatted as markdown.
    - For each action item: write a new row to Action items (table) with Status=Open. Status is owner-controlled after that.
    - Send follow-up: email to attendees (via SMTP, resolves email via participants.json if not in transcript) and/or Slack post to a channel.
    - Agent NEVER marks an Action items row as Done. The owner does that when they complete the task.
    - End of every run, agent writes 1 paragraph to **Status**: transcripts processed, action items extracted, follow-ups sent, anything skipped.

    ## First run

    1. Confirm with the operator: transcript source (local folder or Fireflies), follow-up channel (email, Slack, or both), participants.json content if transcripts don't include attendee emails.
    2. Open Setup guide and follow the install steps. Configure .env + populate participants.json.
    3. Drop a test transcript into TRANSCRIPT_DIR (or confirm a recent Fireflies meeting is within FIREFLIES_LOOKBACK_HOURS). Run python extract_followup.py once.
    4. Confirm Meetings doc has a new section, Action items table has new rows, follow-up landed in email or Slack.

    ## Status

    ### [00:00 UTC] seeded by Dock
    Workspace created from the meeting follow-up and action item extraction template. Agent: read the Setup guide, then confirm transcript source + follow-up channel + participants.json with the operator before the first scan.
    Next: confirm transcript source + follow-up channel + participants.json.

## Outcome

A workspace where every meeting becomes a follow-up email or Slack post + a sectioned meeting log in Dock + one Action items row per task. Nothing said in a meeting falls through the cracks.

**Estimated time:** 45 min setup, ongoing zero touch (transcripts flow in)  
**Difficulty:** beginner  
**For:** Operators, ops leads, and EAs at $5M-$50M revenue companies running 5+ meetings per week.

## What you'll need

Pre-register or install before you start.

- **[Dock](https://trydock.ai)** _(Free plan covers this template; Pro $19/mo unlocks more workspaces.)_ — The workspace itself, the Action items table, the Meetings doc surface, the Status surface.
- **[Anthropic API](https://www.anthropic.com/api)** _(Pay per token, ~$0.01-$0.03 per meeting.)_ — Claude extracts meeting title + attendees + summary + decisions + action items (with owner, due date, priority) from each transcript.
- **[Fireflies.ai (optional)](https://fireflies.ai)** _(Fireflies Pro $18/user/mo; local folder is free.)_ — Transcript source. Records meetings and exposes a GraphQL API. Alternative: a local folder of .txt or .md files.
- **[SMTP (optional)](https://support.google.com/mail/answer/185833)** _(Free with Gmail; SendGrid + Mailgun have free tiers below 100/day.)_ — Email follow-up delivery. Gmail App Passwords work, or any SMTP provider (SendGrid, Mailgun).
- **[Slack (optional)](https://api.slack.com/messaging/webhooks)** _(Free)_ — Alternative or additional follow-up channel. Block Kit message with summary + decisions + action items.
- **[CueAPI (recommended)](https://cueapi.ai)** _(Free tier covers thrice-daily cadence.)_ — Cloud-scheduled 3x-per-weekday cron so the scan keeps running when your laptop is closed.

---

# The template · 5 steps

## Step 1: Pick transcript source + follow-up channel

_Estimated time: 10 min_

Two operator decisions: where do transcripts come from, and where does the follow-up go. Local folder (drag .txt files in) is the simplest path. Fireflies is hands-off but needs an account. Follow-up channel can be email (attendees), Slack (team channel), or both.

### Tasks

- [ ] Ask the operator: do they have Fireflies, Gong, or Zoom AI? If Fireflies, grab the API key from the dashboard. If not, plan on local folder.
- [ ] Ask: where should follow-ups land? Email to attendees (most personal), Slack post (faster + team-visible), or both.
- [ ] If email: get SMTP credentials. Gmail App Passwords work; require 2FA on the Google account first.
- [ ] If Slack: create a bot token with chat:write or use an incoming webhook for a channel.
- [ ] If attendee emails aren't in the transcripts: populate participants.json mapping names (exactly as they appear in transcripts) to email addresses.

> [!CAUTION]
> **Gotchas**
>
> - participants.json name matching. Names must match how they appear in the transcript exactly. After the first run, open the Meetings doc and check the Attendees line; copy those exact strings into participants.json as keys.
> - Self-email. If the operator's name is in participants.json, the script sends them their own follow-up. To skip, remove their entry or add a filter in send_email_followup().

## Step 2: Wire .env + the Python script

_Estimated time: 20 min_

One script: extract_followup.py runs 3x per weekday. Reads new transcripts, runs extract_meeting_data() via Claude, appends to Meetings doc, writes Action items rows, sends follow-up. Idempotent via processed_transcripts.json.

### Tasks

- [ ] Open Setup guide (doc) and copy extract_followup.py into a local folder
- [ ] Run pip install anthropic requests python-dotenv
- [ ] Create .env with DOCK_API_KEY, DOCK_WORKSPACE_SLUG, ANTHROPIC_API_KEY, TRANSCRIPT_DIR (or FIREFLIES_API_KEY + FIREFLIES_LOOKBACK_HOURS), FOLLOWUP_EMAIL=true/false, FOLLOWUP_SLACK=true/false, SMTP_HOST + SMTP_USER + SMTP_PASSWORD (if email), SLACK_BOT_TOKEN + SLACK_CHANNEL (if Slack), PARTICIPANTS_FILE=./participants.json, CLAUDE_MODEL=claude-sonnet-4-6
- [ ] Generate a Dock API key at trydock.ai/settings/api
- [ ] Set up the Action items table column schema explicitly: Meeting, Item, Owner, Due Date, Priority, Status (all text).

> [!CAUTION]
> **Gotchas**
>
> - Long transcripts. The script uses a bookend window (first half + last half, up to 15K chars) to capture both opening context AND end-of-meeting action items. Going larger blows the token budget; smaller misses the wrap-up.
> - FIREFLIES_LOOKBACK_HOURS. Default 6. If the script runs every 4 hours, 6 is the right safety margin. Smaller windows can miss meetings whose transcript finished processing late.

## Step 3: Test with one transcript

_Estimated time: 20 min_

Before scheduling, run a real transcript through the manual path. Drop a .txt file into TRANSCRIPT_DIR, run the script, walk through what got extracted with the operator + an attendee.

### Tasks

- [ ] Drop a recent meeting transcript (.txt or .md) into TRANSCRIPT_DIR. Or confirm a recent Fireflies meeting is within FIREFLIES_LOOKBACK_HOURS.
- [ ] Run python extract_followup.py
- [ ] Open Meetings (doc). Confirm a new section appeared with title + attendees + summary + decisions + action items.
- [ ] Open Action items (table). Confirm one row per extracted task with Owner + Due Date + Priority + Status=Open.
- [ ] Check the follow-up email (or Slack post). Confirm tone, attendees, formatting.
- [ ] Walk through with an attendee: do the action items match what they remember? If not, edit the extract_meeting_data() prompt to bias toward (or away from) implicit commitments.

> [!CAUTION]
> **Gotchas**
>
> - Extraction quality depends on transcript clarity. Calls where everyone said 'sure' instead of 'John will follow up by Friday' produce fewer action items. The agent extracts only explicit assignments; that's intentional.
> - Names in transcripts. Fireflies often gets first names only. participants.json keys should match exactly: if Fireflies says 'John', the key is 'John', not 'John Smith'.

### Agent prompt for this step

```text
Run a first transcript through the pipeline. Load processed_transcripts.json. Fetch new transcripts from TRANSCRIPT_DIR or Fireflies. For each unprocessed transcript: extract_meeting_data() via Claude (title, attendees, summary, decisions, action_items). append_meeting_summary() to Meetings doc. append_action_items() to Action items table with Status=Open. Send follow-up via email + Slack per config. Append transcript ID to processed_transcripts.json. Post a Status entry: transcripts processed, action items extracted, follow-ups sent.
```

## Step 4: Schedule the 3x-per-weekday scan

_Estimated time: 10 min_

Once test runs are clean, schedule. 9 AM catches overnight + early-morning meetings; 1 PM catches mid-morning; 5 PM catches afternoon. Weekdays only by default; flip to every day if you run weekend meetings too.

### Tasks

- [ ] Option A, cron: crontab -e, add `0 9,13,17 * * 1-5 cd /path && source .env && python3 extract_followup.py >> meeting_followup.log 2>&1`
- [ ] Option B, CueAPI: cueapi create --schedule '0 9,13,17 * * 1-5' --timezone 'America/New_York' --name 'meeting-followup' --handler ./extract_followup.py
- [ ] Confirm next day: Status has at least one fresh session entry. If a meeting was held, its transcript got processed.

> [!CAUTION]
> **Gotchas**
>
> - If meetings span timezones, pick the operator's tz for the cron. CueAPI's --timezone flag is explicit.
> - Closed laptop = no run. Switch to CueAPI for cloud-scheduled if your machine isn't always on.

## Step 5: Make the Action items table the team's source of truth

_Estimated time: 5 min one-time_

The whole flow only works if the team actually looks at the Action items table. Pin the URL in Slack, add it to weekly retros, treat any open item past its due date as a follow-up.

### Tasks

- [ ] Pin the Action items URL (trydock.ai/[org]/meeting-followup-action-items/action-items) to the team Slack channel
- [ ] Add the table to weekly retros: walk through anything still Status=Open past its Due Date.
- [ ] Owners flip Status=Done as they complete tasks. Archive Done rows periodically (or filter the view to Open only).
- [ ] If an owner consistently lets items slip, that's a coaching conversation, not a script tune.

> [!CAUTION]
> **Gotchas**
>
> - Owners controlling Status is intentional. The agent never marks Done; only the owner can. Reps appreciate that.
> - Drift between transcripts and assigned owners. If Claude mis-attributes (e.g. assigns to the meeting host when someone else volunteered), edit the Owner column on the row directly. Future meetings re-extract from scratch.

---

## Hand the template to your agent

Paste the prompt below into your agent's permanent system prompt so the agent reads, writes, and maintains this workspace as you work through the steps.

```text
You are the agent running on the "Meeting follow-up and action item extraction" template workspace, connected via MCP at your-org/meeting-followup-action-items.

Your job: 3x per weekday, scan for new transcripts, extract decisions + action items, send follow-up, append Meetings doc section, write Action items rows. Never mark an action item Done.

User-loop protocol:
- You propose. The owner decides. Never mark an Action items row as Done; that's owner-controlled when they complete the task.
- 3x per weekday at 9 AM / 1 PM / 5 PM (or "process new transcripts"): load processed_transcripts.json. Fetch new transcripts from either TRANSCRIPT_DIR (local folder) or Fireflies (FIREFLIES_API_KEY + FIREFLIES_LOOKBACK_HOURS).
- For each transcript whose ID is not in processed_transcripts.json:
  - extract_meeting_data() via Claude: meeting_title, meeting_date, attendees (name + email), summary (2-3 sentences), decisions (list), action_items (list with item + owner + due_date + priority).
  - append_meeting_summary() to Meetings doc: new section with title + date + attendees + summary + decisions + action items as markdown.
  - append_action_items() to Action items table: one row per action item with Status=Open.
  - If FOLLOWUP_EMAIL=true: resolve attendee emails via participants.json + transcript, send via SMTP.
  - If FOLLOWUP_SLACK=true: post Block Kit summary to SLACK_CHANNEL.
  - Append transcript ID to processed_transcripts.json.
- End of every run, write 1 paragraph to Status: transcripts processed, action items extracted, follow-ups sent, anything skipped.

Don't touch:
- processed_transcripts.json from prior runs (only append; never delete).
- An Action items row's Status after creation (owner-controlled; agent only writes Status=Open on creation).
- A Meetings doc section from a prior run.
- participants.json (operator-edited; agent only reads).

First MCP tool calls:
1. list_surfaces(workspace_slug="meeting-followup-action-items")
2. list_rows(workspace_slug="meeting-followup-action-items", surface_slug="action-items")
3. get_doc(workspace_slug="meeting-followup-action-items", surface_slug="status")
```

---

## FAQ

### Does this work without Fireflies?

Yes. Set TRANSCRIPT_DIR to a local folder and drop .txt or .md transcripts in there. The script picks them up on the next scan. Useful for teams that record on Zoom or Otter but don't have a Fireflies account.

### What if the transcript doesn't include attendee emails?

Populate participants.json with name-to-email mappings. The keys must match the names exactly as they appear in the transcript. The Meetings doc section shows the extracted names after the first run; copy those strings into participants.json.

### Does the agent ever mark action items as done?

No. The owner does that. The agent writes new rows with Status=Open. Owners flip to Done as they complete. This is intentional, owners object to a bot closing things out from under them.

### Can I add a Day 14 reminder for overdue action items?

Yes. The Extending section in Setup guide shows the pattern: a second script (overdue_reminder.py) runs daily, reads the Action items table, filters Status=Open + Due Date in the past, sends a Slack DM to each owner with their overdue rows.

### What if Claude extracts action items that aren't real commitments?

Edit the extract_meeting_data() prompt to be stricter: 'Extract only explicit action items where someone said I will or NAME will. Do not infer.' Default already biases toward explicit assignments. If you need implicit + explicit, swap the language the other way.

