Skip to content

cgrinaldi/ynab-status

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ynab-status

Daily email status report for YNAB categories. It pulls your YNAB budget data, computes per-category pacing and weekly allowance, renders a clean HTML/text email, and sends it via Gmail.

This repo uses uv + pyproject.toml for dependency management and includes a GitHub Actions workflow for automated runs.

Features

  • Per-category weekly allowance based on remaining days in month
  • Pacing vs. target spend (slow down / on track / could spend more)
  • Clear status icons and color coding for negative balances and pacing
  • HTML and plain text email output (also written to out/ for inspection)
  • Gmail API integration (OAuth) for sending mail
    • Alternatively: SMTP with Gmail App Password (no OAuth token refresh)

Requirements

  • Python >= 3.11
  • A YNAB Personal Access Token (API key)
  • Gmail OAuth credentials (credentials.json and a generated token.json)
    • Or enable SMTP with a Gmail App Password (no OAuth files needed)
  • Recommended: uv for managing and running the project

Quick Start

  1. Install uv (recommended)

    • macOS / Linux:
      • curl -LsSf https://astral.sh/uv/install.sh | sh
  2. Sync dependencies

    • uv sync
  3. Configure environment

    • Create a .env file at the repo root with at least:

      YNAB_API_KEY=your_ynab_api_key
      GOOGLE_OAUTH_CLIENT_ID=your_google_oauth_client_id
    • Place your Gmail OAuth client file as credentials.json in the repo root. The first run will perform a browser OAuth flow and write token.json next to it.

    • Alternate: if you set GMAIL_APP_PASSWORD in your environment, the app will send via Gmail SMTP and skip OAuth entirely.

  4. Run locally

    • uv run -m app.main

    On first send, Gmail OAuth will open a browser for consent. After that, token.json will be reused.

Configuration

Most knobs live in app/main.py.

  • app/main.py:1 — Set your budget name in BUDGET_NAME.
  • app/main.py:10 — Customize the WATCHLIST mapping of category groups → categories. You can:
    • Use exact names: "Groceries"
    • Use wildcards per group: "*" to include all categories in that group
    • Provide objects to toggle monitoring per item: { "name": "Gifts", "monitor": false }
  • SOFT_WARN_THRESHOLD — Amount below which balance shows an amber warning
  • Pacing controls:
    • PACING_ENABLED — Toggle pacing display and status influence
    • PACING_UPPER_OVER_PCT — Threshold over target spend to show slow down (🐢)
    • PACING_LOWER_UNDER_PCT — Threshold under target spend to show could spend more (🐇)
  • Email settings:
    • SENDER and RECIPIENTS
    • DRY_RUN_WRITE_HTML — When True, also writes out/email.html and out/email.txt

Environment variables are loaded via Pydantic Settings from .env (see app/config.py). Required keys:

  • YNAB_API_KEY — Your YNAB API key
  • GOOGLE_OAUTH_CLIENT_ID — Your OAuth client ID (used by Google libs)

Optional for SMTP (alternate transport):

  • GMAIL_APP_PASSWORD — App Password for the sender Gmail account (2FA required)

Gmail credentials are provided via files expected by app/mailer.py:

  • credentials.json — OAuth client credentials
  • token.json — Generated automatically on the first successful auth flow

What It Sends

app/report.py renders both HTML and plain text with:

  • Days/weeks remaining and percent of month complete
  • Budget last updated timestamp (converted to Pacific Time) and days ago
  • Two sections: Monitoring and Not Monitoring, grouped by YNAB category group
  • For monitored categories: budgeted, spent, balance, target-by-now, pacing, weekly allowance

Running in GitHub Actions

A workflow is provided at .github/workflows/ynab-uv-cron.yml.

  • Secrets you need to set in your repository settings:
    • YNAB_API_KEY
    • GOOGLE_OAUTH_CLIENT_ID
    • If using Gmail API (OAuth):
      • GMAIL_CREDENTIALS_JSON — Contents of your credentials.json
      • GMAIL_TOKEN_JSON — Contents of a working token.json
    • If using SMTP (App Password):
      • GMAIL_APP_PASSWORD — App Password for the SENDER account (see below)
  • The schedule section is currently commented out. You can re-enable and adjust it for your timezone.
  • The job uses uv sync --frozen and runs uv run -m app.main.

Development Notes

Project layout:

  • app/main.py — Orchestration and configuration
  • app/ynab_client.py — Thin wrapper around the ynab SDK
  • app/domain.py — Selection, pacing, weekly allowance, and status logic
  • app/report.py — Jinja templating for HTML/text
  • app/mailer.py — Gmail API send using OAuth
  • app/helpers.py — Utility helpers

Dependencies are declared in pyproject.toml; a uv.lock is present for reproducibility.

Troubleshooting

  • Gmail auth keeps prompting: delete token.json and re-run to regenerate it.
  • Want to avoid token refresh? Enable SMTP by creating a Gmail App Password (Google Account → Security → App passwords), set it as GMAIL_APP_PASSWORD secret, and remove the OAuth-secret steps in the workflow.
  • No categories found: check BUDGET_NAME and ensure group/category names in WATCHLIST match your YNAB exactly.
  • Timeouts with YNAB: ensure YNAB_API_KEY is correct and network access is available.
  • Emails not received: check the Gmail account’s Sent folder and spam; verify SENDER and RECIPIENTS.

Security

  • Do not commit .env, credentials.json, or token.json.
  • Use GitHub Secrets for CI. Rotate keys if you suspect exposure.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages