Sync Granola meeting transcripts into an Obsidian vault as enriched, read-only markdown — with optional AI summaries powered by Claude.
- Fetches all your Granola transcripts via the API
- Writes Obsidian-flavored markdown with
[[wiki-links]],#tags, and Dataview-compatible frontmatter - Organizes files by date:
Vault/Granola/2025/01/2025-01-15_standup.md - Preserves raw JSON API responses for archival
- Incremental sync — only fetches documents that changed since last run
- Generates AI meeting summaries using Claude (Anthropic API), with Obsidian-native formatting
- Extracts a knowledge graph — People, Concepts, and Projects notes with
[[wiki-links]]across meetings - Resumable summarization — picks up where it left off if interrupted
cargo install --path .Baez needs two separate API keys: one for Granola (to fetch transcripts) and one for Anthropic (to generate AI summaries). They are configured independently.
The Granola token is used to authenticate with the Granola API and fetch your meeting transcripts. Baez resolves it in this order:
-
--tokenflag (highest priority)baez sync --token <granola-session-token>
-
BAEZ_GRANOLA_TOKENenvironment variableexport BAEZ_GRANOLA_TOKEN=<granola-session-token> baez sync
-
BEARER_TOKENenvironment variable (deprecated, useBAEZ_GRANOLA_TOKEN) -
Config file (
~/.config/baez/config.json→granola_tokenfield) -
Granola's local session file (automatic, no setup needed) If you're logged into the Granola desktop app on macOS, baez reads the token directly from
~/Library/Application Support/Granola/supabase.json. This is the default — if you use the Granola app, you don't need to configure anything.
The Anthropic API key is used to generate meeting summaries via Claude. This is optional — sync works without it, you'll just get transcripts without summaries.
-
BAEZ_ANTHROPIC_API_KEYenvironment variable (highest priority)export BAEZ_ANTHROPIC_API_KEY=sk-ant-... baez sync -
ANTHROPIC_API_KEYenvironment variable (also accepted, standard Anthropic convention)export ANTHROPIC_API_KEY=sk-ant-... baez sync -
Config file (
~/.config/baez/config.json→anthropic_api_keyfield) -
macOS Keychain (persistent, recommended on macOS)
baez set-api-key sk-ant-...
This stores the key in the macOS system keychain under the service
baezwith the account nameanthropic_api_key. The key persists across terminal sessions and reboots.
If no Anthropic API key is found, baez sync prints a warning and continues
without summarization. You can also explicitly skip summaries with
--no-summarize.
# Option 1: Pass vault path directly
baez sync --vault /path/to/your/obsidian-vault
# Option 2: Set environment variable
export BAEZ_VAULT=/path/to/your/obsidian-vault
baez sync
# Option 3: Save in config file (supports all settings)
mkdir -p ~/.config/baez
echo '{"vault": "/path/to/your/obsidian-vault"}' > ~/.config/baez/config.json
baez syncAll settings can be stored in ~/.config/baez/config.json:
{
"vault": "/path/to/your/obsidian-vault",
"granola_token": "eyJ...",
"anthropic_api_key": "sk-ant-..."
}All fields are optional. CLI flags and environment variables take precedence over config file values.
Security note: If you store API keys in the config file, restrict its permissions so only your user can read it:
chmod 600 ~/.config/baez/config.jsonBaez will print a warning at startup if the config file contains keys and is
readable by other users. On macOS, the keychain (baez set-api-key) is the
most secure option for the Anthropic API key.
baez sync Sync all documents (with AI summaries if key is set)
baez sync --force Force re-sync, ignoring cache
baez sync --no-summarize Sync without AI summaries
baez sync --dry-run Preview what would be synced
baez summarize-all Batch-summarize all unsummarized docs (no API fetch needed)
baez summarize-all --force Re-summarize everything
baez summarize-all --dry-run Preview what would be summarized
baez list List all documents
baez fetch <id> Fetch a specific document
baez open Open vault directory
baez fix-dates Fix file modification dates
Summaries are generated during baez sync when an Anthropic API key is
available. If sync is interrupted, re-running baez sync or
baez summarize-all picks up where it left off — already-summarized docs are
tracked in a separate summary cache and skipped automatically.
baez summarize <doc-id> Print summary to stdout
baez summarize <doc-id> --save Update the ## Summary section in the markdown file
baez summarize-all Batch-summarize all unsummarized docs from local files
baez summarize-all --force Re-summarize everything (e.g. after changing model)summarize-all reads from the raw JSON files already on disk — it never hits
the Granola API. This makes it safe to run repeatedly and useful for catching up
after enabling summarization on an existing vault.
Configure the model and input limits:
baez set-config --show Show current configuration
baez set-config --model claude-sonnet-4-20250514 Change model
baez set-config --context-window 300000 Set max input size (chars)
baez set-config --prompt-file my-prompt.txt Use a custom promptThe default model is claude-opus-4-6 with a 600,000 character input limit
(~150K tokens) and 8,192 max output tokens.
When summarization is enabled, baez extracts entities from each meeting and creates interconnected notes in your vault:
Vault/
├── People/ # One note per person mentioned across meetings
│ └── Alice Smith.md
├── Concepts/ # Reusable ideas and patterns
│ └── API Design.md
└── Projects/ # Projects discussed in meetings
└── Project Atlas.md
Each entity note tracks which meetings it appeared in via ## Sources links.
Meeting notes link back via related frontmatter fields. The result is a
navigable knowledge graph in Obsidian's graph view — people, concepts, and
projects connected through meetings.
Vault/
├── People/ # Entity notes (auto-created)
├── Concepts/
├── Projects/
└── Granola/
├── 2025/
│ ├── 01/
│ │ ├── 2025-01-15_standup.md
│ │ └── 2025-01-16_planning.md
│ └── 02/
│ └── ...
└── .baez/
├── raw/ # Raw JSON API responses
├── tmp/ # Atomic write temp dir
├── summary_config.json # Summarization settings
├── .sync_cache.json # Incremental sync state
└── .summary_cache.json # Summarization progress tracking
Each document gets Dataview-compatible frontmatter:
doc_id: abc123
source: granola
date: "2025-01-15"
created: 2025-01-15T10:00:00Z
title: Sprint Planning
attendees: [Alice, Bob]
duration_minutes: 30
tags: [planning]
related:
- "[[Alice Smith]]"
- "[[API Design]]"
status: substantive
generator: baezThe body includes [[wiki-links]] for attendees and speakers, #granola and
#meeting/* tags, an AI-generated ## Summary section (with Key Decisions,
Action Items, Discussion Highlights, and Open Questions), user notes, and the
full transcript.
MIT