> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Meeting Minutes Action Items

> Extract action items and generate structured meeting minutes from transcripts

# Meeting Minutes Action Items

Extract action items and generate structured meeting minutes from meeting transcripts.

## Problem Statement

**Who:** Project managers, team leads, executive assistants\
**Why:** Manual note-taking misses details. Automated extraction ensures nothing falls through the cracks.

## What You'll Build

A recipe that analyzes meeting transcripts and produces structured minutes with action items.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Input[📝 Transcript] --> Analyze[Analyze Content]
    Analyze --> Extract[Extract Actions]
    Extract --> Format[Format Minutes]
    Format --> Output[📋 Minutes + Actions]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff

    class Input,Output input
    class Analyze,Extract,Format process
```

### Input/Output Contract

| Input | Type | Required | Description |
| - | - | - | - |
| `transcript_text` | string | Yes | Meeting transcript |
| `attendees` | array | No | List of attendee names |
| `format` | string | No | Output format: `markdown` or `json` |

| Output | Type | Description |
| - | - | - |
| `minutes` | string | Formatted meeting minutes |
| `action_items` | array | Extracted action items with assignees |
| `ok` | boolean | Success indicator |

## Prerequisites

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export OPENAI_API_KEY=your_key_here
pip install praisonaiagents
```

## Step-by-Step Build

**1. Create Recipe Directory**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
mkdir -p ~/.praisonai/templates/meeting-minutes-action-items
cd ~/.praisonai/templates/meeting-minutes-action-items
```

**2. Create TEMPLATE.yaml**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
name: meeting-minutes-action-items
version: "1.0.0"
description: "Extract action items from meeting transcripts"
author: "PraisonAI"
license: "MIT"

tags:
  - meetings
  - productivity
  - action-items

requires:
  env:
    - OPENAI_API_KEY
  packages:
    - praisonaiagents

inputs:
  transcript_text:
    type: string
    description: "Meeting transcript text"
    required: true
  attendees:
    type: array
    description: "List of attendee names"
    required: false
    default: []
  format:
    type: string
    description: "Output format"
    required: false
    default: "markdown"
    enum:
      - markdown
      - json

outputs:
  minutes:
    type: string
    description: "Formatted meeting minutes"
  action_items:
    type: array
    description: "List of action items"
  ok:
    type: boolean
    description: "Success indicator"

cli:
  command: "praison recipes run meeting-minutes-action-items"
  examples:
    - 'praison recipes run meeting-minutes-action-items --input ''{"transcript_text": "..."}'''

safety:
  dry_run_default: false
  requires_consent: false
  overwrites_files: false
  network_access: true
  pii_handling: true
```

**3. Create recipe.py**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# recipe.py
import json
from praisonaiagents import Agent, Task, AgentTeam

def run(input_data: dict, config: dict = None) -> dict:
    """Extract action items from meeting transcript."""
    transcript_text = input_data.get("transcript_text")
    attendees = input_data.get("attendees", [])
    output_format = input_data.get("format", "markdown")
    
    if not transcript_text:
        return {"ok": False, "error": {"code": "MISSING_INPUT", "message": "transcript_text is required"}}
    
    try:
        attendees_str = ", ".join(attendees) if attendees else "Not specified"
        
        # Create analyzer agent
        analyzer = Agent(
            name="Meeting Analyst",
            role="Meeting Content Specialist",
            goal="Analyze meeting content and identify key points",
            instructions="""
            You are a meeting analysis expert.
            - Identify main topics discussed
            - Note decisions made
            - Find commitments and deadlines
            - Track who said what when relevant
            """,
        )
        
        # Create action extractor
        extractor = Agent(
            name="Action Extractor",
            role="Action Item Specialist",
            goal="Extract clear, actionable items",
            instructions=f"""
            You are an action item extraction expert.
            Known attendees: {attendees_str}
            
            For each action item, identify:
            - Task description
            - Assignee (if mentioned)
            - Due date (if mentioned)
            - Priority (if implied)
            
            Output as JSON array.
            """,
        )
        
        # Create formatter
        formatter = Agent(
            name="Minutes Formatter",
            role="Documentation Specialist",
            goal=f"Format minutes in {output_format}",
            instructions=f"""
            You are a documentation specialist.
            Format meeting minutes in {output_format} format.
            
            Include:
            - Meeting summary
            - Key discussion points
            - Decisions made
            - Action items section
            """,
        )
        
        # Define tasks
        analyze_task = Task(
            name="analyze_meeting",
            description=f"Analyze this meeting transcript:\n\n{transcript_text[:8000]}",
            expected_output="Meeting analysis with key points",
            agent=analyzer,
        )
        
        extract_task = Task(
            name="extract_actions",
            description="Extract action items from the meeting analysis",
            expected_output='JSON array: [{"task": "...", "assignee": "...", "due": "...", "priority": "..."}]',
            agent=extractor,
            context=[analyze_task],
        )
        
        format_task = Task(
            name="format_minutes",
            description=f"Format complete meeting minutes in {output_format}",
            expected_output=f"Formatted meeting minutes in {output_format}",
            agent=formatter,
            context=[analyze_task, extract_task],
        )
        
        # Execute
        agents = AgentTeam(
            agents=[analyzer, extractor, formatter],
            tasks=[analyze_task, extract_task, format_task],
        )
        
        result = agents.start()
        
        # Parse action items
        actions_text = result.get("extract_actions", "[]")
        action_items = parse_action_items(actions_text)
        
        return {
            "ok": True,
            "minutes": result.get("format_minutes", ""),
            "action_items": action_items,
            "artifacts": [],
            "warnings": [],
        }
        
    except Exception as e:
        return {"ok": False, "error": {"code": "PROCESSING_ERROR", "message": str(e)}}


def parse_action_items(text: str) -> list:
    """Parse action items from agent output."""
    import re
    try:
        match = re.search(r'\[.*\]', text, re.DOTALL)
        if match:
            return json.loads(match.group())
    except json.JSONDecodeError:
        pass
    
    # Fallback: parse bullet points
    items = []
    for line in text.split('\n'):
        line = line.strip()
        if line.startswith(('-', '*', '•')) and len(line) > 2:
            items.append({"task": line[1:].strip(), "assignee": None, "due": None})
    
    return items
```

**4. Create test\_recipe.py**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# test_recipe.py
import pytest
from recipe import run, parse_action_items

def test_missing_transcript():
    result = run({})
    assert result["ok"] is False
    assert result["error"]["code"] == "MISSING_INPUT"

def test_parse_action_items_json():
    text = '[{"task": "Review doc", "assignee": "John"}]'
    items = parse_action_items(text)
    assert len(items) == 1
    assert items[0]["task"] == "Review doc"

def test_parse_action_items_bullets():
    text = "- Review the document\n- Send email to client"
    items = parse_action_items(text)
    assert len(items) == 2

@pytest.mark.integration
def test_end_to_end():
    transcript = """
    John: Let's discuss the Q4 roadmap.
    Sarah: I think we should prioritize the mobile app.
    John: Agreed. Sarah, can you draft a proposal by Friday?
    Sarah: Sure, I'll have it ready.
    John: Great. Mike, please review the budget.
    Mike: Will do, I'll send it by Monday.
    """
    
    result = run({
        "transcript_text": transcript,
        "attendees": ["John", "Sarah", "Mike"],
        "format": "markdown"
    })
    
    assert result["ok"] is True
    assert len(result["action_items"]) > 0
```

## Run Locally

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Basic usage
praison recipes run meeting-minutes-action-items \
  --input '{"transcript_text": "Meeting transcript here..."}'

# With attendees
praison recipes run meeting-minutes-action-items \
  --input '{"transcript_text": "...", "attendees": ["John", "Sarah"], "format": "json"}'
```

## Deploy & Integrate: 6 Integration Models

> [tab] Model 1: Embedded SDK
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> from praisonai import recipe
>
> # Load or define transcript
> transcript = "Meeting notes: John said we need to finish the project by Friday..."
>
> result = recipe.run(
>     "meeting-minutes-action-items",
>     input={
>         "transcript_text": transcript,
>         "attendees": ["John", "Sarah", "Mike"]
>     }
> )
>
> if result.ok:
>     for item in result.output["action_items"]:
>         print(f"Task: {item['task']}, Assignee: {item['assignee']}")
> ```
>
> > [!WARNING]
> >
> > **Safety:** Meeting content may contain sensitive business information.

> [tab] Model 2: CLI Invocation
>
> ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> cat meeting_transcript.txt | praison recipes run meeting-minutes-action-items \
>   --input '{"transcript_text": "'"$(cat)"'"}' --json
> ```

> [tab] Model 3: Plugin Mode
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> class MeetingPlugin:
>     def extract_actions(self, transcript, attendees=None):
>         from praisonai import recipe
>         return recipe.run(
>             "meeting-minutes-action-items",
>             input={"transcript_text": transcript, "attendees": attendees or []}
>         )
> ```

> [tab] Model 4: Local HTTP Sidecar
>
> ```javascript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> const response = await fetch('http://localhost:8765/recipes/meeting-minutes-action-items/run', {
>   method: 'POST',
>   body: JSON.stringify({
>     transcript_text: transcript,
>     format: 'json'
>   })
> });
> ```

> [tab] Model 5: Remote Managed Runner
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> response = requests.post(
>     "https://api.meeting-tools.com/extract-actions",
>     headers={"Authorization": f"Bearer {api_key}"},
>     json={"transcript_text": transcript}
> )
> ```

> [tab] Model 6: Event-Driven
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> def on_meeting_ended(event):
>     import queue as q
>     job_queue = q.Queue()  # Replace with SQS/RabbitMQ in production
>     job_queue.put({
>         "recipe": "meeting-minutes-action-items",
>         "input": {
>             "transcript_text": event['transcript'],
>             "attendees": event['participants']
>         },
>         "callback_url": f"https://api.example.com/meetings/{event['meeting_id']}/minutes"
>     })
> ```

## Troubleshooting

> [accordion] Missing action items
>
> Ensure the transcript clearly states commitments. Phrases like "I will" or "by Friday" help extraction.

> [accordion] Wrong assignees
>
> Provide the attendees list to help the model match names correctly.

## Next Steps

* Prefer starting from an audio file? See **[Meeting Notes Agent](https://praison.ai/docs/features/meeting-notes-agent.md)**.
* **[Podcast Transcription Cleaner](https://praison.ai/docs/examples/recipe-examples/podcast-transcription-cleaner.md)** - Clean up meeting recordings
* **[Document Summarizer](https://praison.ai/docs/examples/recipe-examples/document-summarizer-with-citations.md)** - Summarize meeting documents


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.