Telegram AI Agent for Income and Expense Management with a comprehensive Web Dashboard featuring Passkey/WebAuthn support for secure, passwordless authentication.
You can self-host it following the Developer section down below.
Cashout is an intelligent Telegram bot that leverages AI to make expense tracking effortless. Simply send a message in natural language, and the bot will understand and categorize your transactions automatically.
- Intelligent Intent Routing: Just type naturally, no commands needed. The AI understands whether you want to add a transaction, check your weekly summary, search, edit, delete, or export. Simply say "show me this week", "delete expense" or "irish pub 4.50" and the bot figures out the rest. If there is no match or you prefer to do otherwise, you can always fall back to a completely deterministic and classic flow.
- Smart Categorization: Automatically assigns the right category based on your description.
- Duplicate Detection: The AI detects when a new transaction looks like a recent one already saved and asks you to confirm before storing it, preventing accidental double-entries.
- Flexible Date Recognition: Understands various date formats (dd/mm, dd-mm-yyyy, "yesterday", etc.).
- Multi-language Support: Works with transaction descriptions in any language.
- Quick Entry: Add expenses and income with a single message.
- Inline Editing: Modify amount, category, description, or date before confirming.
- Bulk Operations: Edit or delete existing transactions with paginated navigation.
- Transaction Types: Track both expenses (18 categories) and income (2 categories).
- Search and Full Listing: Find transactions by full text search and category or full listing.
- Export Functionality: Download all your transactions as CSV files.
- Weekly Recap: Get detailed breakdowns of your current week's spending.
- Monthly Summary: View month-by-month financial performance with category breakdowns.
- Yearly Overview: See annual trends and top spending categories.
- Balance Tracking: Instant calculation of income vs expenses for any period.
- Category Analysis: Understand where your money goes with percentage breakdowns.
- Per-Category Budgets: Set monthly spending limits for any expense category.
- Live Tracking: See current month progress against each budget at a glance.
- Visual Feedback: Progress bars and clear status when you are close to or over budget.
- Web-Managed: Create, edit, and delete budgets directly from the dashboard.
- Multiple Authentication Methods:
- Telegram-based login with verification codes.
- Email-based passwordless authentication.
- Passkey/WebAuthn support for passwordless biometric login.
- Transaction Management:
- Add new transactions directly from the web interface, with auto-save on creation.
- Edit and delete existing transactions inline.
- View detailed transaction history with search and filtering.
- Monthly navigation with intuitive controls.
- Visual Analytics:
- Real-time balance, income, and expense statistics.
- Interactive charts for category breakdowns and monthly trends.
- Transaction counts and summaries.
- Budget Management:
- Configure monthly budgets per category from the dashboard.
- Track usage in real time with progress indicators.
- Security Features:
- Rate-limited authentication endpoints.
- Secure session management with configurable duration.
- Support for multiple passkeys per user.
- Passkey management (register, list, delete).
- Automated Weekly Recaps: Receive your previous week's summary every Monday.
- Automated Monthly Recaps: Receive your previous month's summary on the 1st of each month.
- Intelligent Scheduling: Only sends reminders to active users.
- Reliable Delivery: Built-in retry mechanism for failed notifications.
/start- Initialize the bot and see the main menu/edit- Edit an existing transaction/delete- Delete a transaction/list- View all transactions (paginated)/search- Search transactions by description/week- Get current week's financial summary/month- Get current month's financial summary/year- Get current year's financial summary/export- Export all transactions to CSV
- Intuitive Interface: Clean inline keyboards for all operations, with a homogeneous category selector across add, edit, search, and budget flows.
- Smart Navigation: Year/month selectors for browsing historical data, with the year shown in list and search results to avoid ambiguity.
- Pagination: Handle large transaction lists with ease.
- Live Typing Indicator: The bot signals "typing..." while the AI thinks, so you always know it is working.
- Quick Actions: Home screen with instant access to all major functions, kept stable to avoid flicker on updates.
- Cancel Anytime: Every operation can be cancelled mid-flow.
- Database Migrations: Version-controlled schema management
- Webhook & Polling Support: Flexible deployment options.
- Development Tools: Built-in database seeder for testing.
- Modular Architecture: Clean separation of concerns for easy maintenance.
- Configurable Access: Optional user whitelist for private deployments.
- Go 1.26 or higher
- PostgreSQL Database
- Access to an OpenAI-compatible API model, with its API Key and Endpoint (e.g. DeepSeek, OpenAI, etc.)
- Clone the repository:
git clone https://github.com/alainrk/cashout.git
cd cashout- Install dependencies:
go mod downloadcp .env.example .envCopy the example .env file in the project root (or set environment variables) and edit it accordingly:
TELEGRAM_BOT_API_TOKEN='XXXXXXXXXX:AAAA_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
DATABASE_URL='postgres://postgres:postgres@localhost:5432/postgres'
OPENAI_API_KEY='sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
OPENAI_BASE_URL='https://api.deepseek.com/v1'
LLM_MODEL='deepseek-v4-flash'
LLM_DISABLE_THINKING='true'
RUN_MODE='webhook' # webhook or polling
WEBHOOK_DOMAIN=''
WEBHOOK_SECRET=''
WEBHOOK_HOST='localhost'
WEBHOOK_PORT='8080'
LOG_LEVEL='info'
# Dev purpose, comma separated. Keep it empty to allow all
ALLOWED_USERS=''
# Seed purpose - set the Telegram ID of the user to seed transactions for
SEED_USER_TG_ID=''
# Web Server Configuration
WEB_HOST=localhost
WEB_PORT=8081
# Session Configuration (optional)
SESSION_SECRET=your-random-session-secret-here
SESSION_DURATION=24h
# Email Service Configuration (for passwordless email login)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
EMAIL_FROM=your-email@gmail.com
# WebAuthn Configuration (for passkey support)
WEBAUTHN_RP_NAME=Cashout
WEBAUTHN_RP_ID=localhost
WEBAUTHN_ORIGIN=http://localhost:8081Spin up local infrastructure:
docker compose up -dCashout uses a version-based migration system to manage database schema changes.
Use the following commands to manage database migrations:
# Run all pending migrations
go run ./cmd/migrate/main.go -command up
# Run all pending migrations with another .env file
go run ./cmd/migrate/main.go -command up -env .prod.env
# Create a new migration just by copy-pasting a previous one and editing it accordingly
cp internal/migrations/versions/001*.go internal/migrations/versions/00X_your_migration.goFor migrations that support rollback, use the RegisterMigrationWithRollback function:
func init() {
migrations.RegisterMigrationWithRollback("003", "Add email column",
add_email_column, rollback_email_column)
}
func rollback_email_column(tx *gorm.DB) error {
return tx.Exec(`ALTER TABLE users DROP COLUMN email`).Error
}# Build the bot
make build
# Run the bot
make run
# Run the bot with live reloading (requires Air)
make run/live# Build the web server
make build-web
# Run the web server
make run-web
# Run the web server with live reloading
make run/live-web# Build both applications
make build-all
# Build both for Linux
make build-linux-all
# Note: Running both requires two terminals
# Terminal 1: make run
# Terminal 2: make run-webThe Dev DB Seeder generates test transaction data for development:
# Set the user's Telegram ID you want to seed data for
export SEED_USER_TG_ID=123456789
# Seed the database with random transactions
make db/seedThe seeder will:
- Generate 5 years of transaction history.
- Create 90% expenses and 10% income transactions.
- Distribute transactions across all categories.
- Ensure at least one salary per month.
- Delete existing transactions before seeding (idempotent).
The project includes a complete Docker Compose setup:
# Start all services (database, bot, web server)
docker compose up -d
# View logs
docker compose logs -f
# Stop all services
docker compose downThis will start:
- PostgreSQL database on port 5432
- Telegram bot (webhook mode on port 8080)
- Web dashboard on port 8081
- Automatic database migrations
The bot can run both in webhook and polling mode.
Webhook Mode:
RUN_MODE='webhook'
WEBHOOK_DOMAIN='https://your-domain.com'
WEBHOOK_SECRET='xxxyyyzzz'
WEBHOOK_PORT='8080'Polling Mode:
RUN_MODE='polling'The web server runs independently and can be configured:
WEB_HOST=0.0.0.0 # For production
WEB_PORT=8081
SESSION_SECRET=your-random-session-secret-here
SESSION_DURATION=24hAny OpenAI compatible API LLM can be used:
Example with DeepSeek:
OPENAI_API_KEY='sk-xxx'
OPENAI_BASE_URL='https://api.deepseek.com/v1'
LLM_MODEL='deepseek-v4-flash'
LLM_DISABLE_THINKING='true'Example with OpenAI:
OPENAI_API_KEY='sk-xxx'
OPENAI_BASE_URL='https://api.openai.com/v1'
LLM_MODEL='gpt-4'The web dashboard supports three authentication methods:
-
Telegram Login (code-based):
- Enter your Telegram username.
- Check Telegram for a 6-digit verification code.
- Enter the code to access your dashboard.
-
Email Login (passwordless):
- Enter your registered email address.
- Check your email for a 6-digit verification code.
- Enter the code to access your dashboard.
-
Passkey Login (WebAuthn):
- Register a passkey from your dashboard settings after initial login.
- Use biometric authentication (fingerprint, face recognition) on subsequent logins.
- No codes needed - instant secure access.
- Access: Navigate to
http://localhost:8081(or your configured domain). - Login: Choose your preferred authentication method.
- Dashboard: View your financial data with month navigation.
- Statistics: See real-time balance, income, expenses, and transaction counts.
- Transactions:
- Add new transactions directly from the web interface.
- Browse detailed transaction history with search and filtering.
- View transactions by category.
- Passkey Management: Register, view, and delete passkeys for your account.
The web dashboard provides a complementary interface to the Telegram bot, offering:
- Better visualization for large datasets.
- Month-by-month navigation.
- Desktop and mobile-friendly transaction management.
- Multiple secure authentication options.
- Direct transaction creation without needing Telegram.
The dashboard endpoints under /web/api/* also accept programmatic clients via
Authorization: Bearer <token> in addition to the existing browser session cookie.
There is no token-management UI on purpose (as of now); tokens are inserted manually by an operator. The plaintext token is shown to the user once and only its SHA-256 hex digest is stored.
# Generate locally:
TOKEN="cshk_$(openssl rand -base64 24 | tr -d '=+/' | head -c 32)"
HASH=$(printf %s "$TOKEN" | shasum -a 256 | awk '{print $1}')
echo "token=$TOKEN"
# Then in psql, replace <tg_id> with the target user's Telegram ID:
# INSERT INTO api_tokens (tg_id, name, token_hash, prefix)
# VALUES (<tg_id>, 'my-cli', '<HASH>', substring('<TOKEN>' from 1 for 8));Optional expires_at is supported; leave NULL for non-expiring tokens.
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8081/web/api/stats?month=2026-05"The spec lives at api/swagger.{yaml,json} and is generated from swaggo/swag
annotations on the handlers. SDKs are committed under sdks/{python,go,typescript}/.
make openapi # regenerate the spec
make sdks # regenerate spec + all three SDKs
make sdk-python # individual languages
make sdk-go
make sdk-tsSDK generation uses the @openapitools/openapi-generator-cli npm wrapper and
requires npx and Java 11+ on PATH.
The TypeScript SDK is generated and committed under sdks/typescript/, but a
proper consumer install path is on standby until there's a concrete need.
Unlike pip and Go, npm has no first-class way to install a package from a
subdirectory of a git repo, so shipping this SDK to external consumers requires
extra work — either publishing to npm under a real scope, or distributing
packed tarballs. That will be wired up when the first consumer needs it; for
now the generated code sits in the repo as a starting point and is kept in
sync with the OpenAPI spec by make sdk-ts.
The Python SDK is a standard PEP 621 package rooted at sdks/python/. uv and
pip can install it directly from this repo using the subdirectory fragment:
# Add it to a uv-managed project
uv add "git+https://github.com/alainrk/cashout.git#subdirectory=sdks/python"
# Or one-off into the current environment
uv pip install "git+https://github.com/alainrk/cashout.git#subdirectory=sdks/python"
# Plain pip works too
pip install "git+https://github.com/alainrk/cashout.git#subdirectory=sdks/python"To pin to a specific commit or tag, append @<ref> before the #:
uv add "git+https://github.com/alainrk/cashout.git@v0.1.0#subdirectory=sdks/python"Usage:
import cashout_sdk
from cashout_sdk.api.transactions_api import TransactionsApi
cfg = cashout_sdk.Configuration(
host="http://localhost:8081/web",
access_token="cshk_...",
)
with cashout_sdk.ApiClient(cfg) as client:
stats = TransactionsApi(client).api_stats_get(month="2026-05")
print(stats)The Go SDK is a Go submodule rooted at sdks/go/. Its module path matches its
on-disk location, so it is go get-able directly from this repo:
go get github.com/alainrk/cashout/sdks/go@latestimport (
"context"
cashout "github.com/alainrk/cashout/sdks/go"
)
func main() {
cfg := cashout.NewConfiguration()
cfg.Servers = cashout.ServerConfigurations{{URL: "http://localhost:8081/web"}}
cfg.DefaultHeader["Authorization"] = "Bearer cshk_..."
client := cashout.NewAPIClient(cfg)
stats, _, err := client.TransactionsAPI.
ApiStatsGet(context.Background()).
Month("2026-05").
Execute()
_ = stats; _ = err
}Versioning. Because the SDK lives in a subdirectory, Go expects git tags to be prefixed with the subdirectory path when you cut releases:
git tag sdks/go/v0.1.0
git push origin sdks/go/v0.1.0Consumers then pin via:
go get github.com/alainrk/cashout/sdks/go@v0.1.0Until a sdks/go/vX.Y.Z tag exists, @latest resolves to a pseudo-version
synthesized from the commit (v0.0.0-<timestamp>-<sha>). That works for early
adopters but isn't a stable release.
# Run all tests
make test
# Run tests with coverage
make test-coverage
# Run tests in CI mode
make test-ci
# Run security checks
make secThis project is licensed under the MIT License - see the LICENSE file for details.