A FastAPI service to transcribe and summarize your daily meetings using OpenAI Whisper and GPT-4o mini, with optional Notion integration.
- Features
- Quickstart
- Notion Integration
- Custom Prompts
- OpenAI-compatible platforms
- Project Structure
- Deployment
- Contributing
- License
- π§ Audio transcription via OpenAI Whisper (mp3, m4a, wav, and any format supported by ffmpeg)
- π€ Structured Markdown summary generation via GPT-4o mini
- π¦ Optional Notion integration: automatically publish summaries to your Notion database
- π Multilingual β language is configurable via
PROMPT_CONFIG - πͺ FastAPI HTTP API with interactive OpenAPI docs at
/docs - π Token usage tracking in every response
- π¦ Structured logging with Loguru
git clone https://github.com/mael-app/daily-meeting-transcriber.git
cd daily-meeting-transcriber
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtffmpeg must be installed on your system for audio processing (
brew install ffmpeg/apt install ffmpeg).
Copy .env.example to .env and fill in your values:
cp .env.example .env| Variable | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes | API key for your OpenAI-compatible platform |
PROMPT_CONFIG |
Yes | Prompts as a JSON string (see Custom Prompts) |
NOTION_TOKEN |
No | Notion integration token |
NOTION_DB_SCHEMA |
No* | Notion database schema as a JSON string |
NOTION_TITLE |
No | Title for created Notion pages (default: Daily) |
NOTION_CATEGORY |
No | Category select value for Notion pages (default: Standup) |
OPENAI_WHISPER_ENDPOINT |
No | Whisper-compatible transcription endpoint (default: OpenAI) |
OPENAI_CHAT_ENDPOINT |
No | Chat completions endpoint (default: OpenAI) |
WHISPER_MODEL |
No | Transcription model (default: whisper-1) |
CHAT_MODEL |
No | Chat model (default: gpt-4o-mini) |
*Required only if NOTION_TOKEN is set and no schema is uploaded per-request.
# With hot reload
python -m uvicorn app:app --reload --port 8080
# Or with Docker Compose (loads .env automatically)
docker-compose up --buildOpen http://localhost:8080/docs for interactive documentation.
Endpoints:
GET /healthβ HealthcheckPOST /process-audioβ Transcribe and summarize an audio file
Example:
curl -X POST "http://localhost:8080/process-audio" \
-F "file=@meeting.m4a"Response:
{
"tokens": 1234,
"transcript_success": true,
"markdown": "### Work from yesterday\n- ...",
"notion_sent": false
}To enable Notion publishing, set NOTION_TOKEN and NOTION_DB_SCHEMA.
If NOTION_TOKEN is not set, Notion is skipped entirely and the response still contains the transcript and summary.
Go to https://www.notion.so/profile/integrations, create a new integration, and copy the Internal Integration Token β this is your NOTION_TOKEN.
Open your Notion database in the browser. The URL looks like:
https://www.notion.so/yourworkspace/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?v=...
The 32-character string before the ? is your database ID.
Alternatively, use the Notion API to retrieve it:
curl -X GET "https://api.notion.com/v1/search" \
-H "Authorization: Bearer secret_..." \
-H "Notion-Version: 2022-06-28" \
-H "Content-Type: application/json" \
-d '{"filter": {"value": "database", "property": "object"}}'Look for the "id" field in the response matching your database.
In Notion, open the database β click ... (top right) β Connections β add your integration.
export NOTION_TOKEN="secret_..."
export NOTION_DB_SCHEMA='{"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}'The NOTION_DB_SCHEMA value only needs to contain the id (or database_id) key. The service reads that field to identify the target database.
You can also upload the schema per-request via the optional notion_schema field:
curl -X POST "http://localhost:8080/process-audio" \
-F "file=@meeting.m4a" \
-F "notion_schema=@schema.json"PROMPT_CONFIG is required. Set it to a JSON string containing your prompts:
export PROMPT_CONFIG='{
"system_prompt": "You are an assistant that summarizes developer standups.",
"user_prompt": "Summarize the following transcript:\n---\n{transcript}\n---",
"language": "en"
}'The {transcript} placeholder is required in user_prompt. The language value is passed directly to the Whisper API.
On Kubernetes, use a ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: transcriber-config
data:
PROMPT_CONFIG: |
{"system_prompt": "...", "user_prompt": "...\n---\n{transcript}\n---", "language": "en"}OPENAI_API_KEY, OPENAI_WHISPER_ENDPOINT, and OPENAI_CHAT_ENDPOINT work with any platform that exposes an OpenAI-compatible API β Azure OpenAI, Together AI, local inference servers, etc.
Example with a local server:
OPENAI_API_KEY="local"
OPENAI_WHISPER_ENDPOINT="http://localhost:8000/v1/audio/transcriptions"
OPENAI_CHAT_ENDPOINT="http://localhost:8000/v1/chat/completions"
WHISPER_MODEL="whisper-large-v3"
CHAT_MODEL="llama-3.1-8b-instruct"app.py FastAPI entrypoint
services/
audio_service.py Request orchestrator
transcription_service.py Whisper API integration
summary_service.py GPT-4o mini integration
notion_service.py Notion API integration
utils/
config.py App configuration loader
env.py Environment variable helper
The CI/CD pipeline (.github/workflows/docker-publish.yml) builds a multi-arch Docker image (linux/amd64, linux/arm64) and pushes it to GitHub Container Registry (ghcr.io) on every push to main.
See CONTRIBUTING.md.
See LICENSE.