Spendly is a personal finance tracker that lets you manage money owed between friends and family using natural language. Say things like "Gave Rahul 5k for groceries" or "Dinner with Priya and Amit, 3200, I paid" — the AI agent parses your message, handles splits, tracks recurring debts with monthly deductions, and manages partial payments. It understands Hinglish, disambiguates when someone has multiple debts, and asks for clarification when your message is vague. Everything works through a Telegram bot or a React web dashboard backed by the same API.
graph TD
TG([Telegram User]) -->|message| BH[Bot Handler]
BH -->|Runner.run| AG[OpenAI Agent<br/>gpt-4o-mini]
AG -->|tool calls| TOOLS[Agent Tools]
TOOLS -->|CRUD| REPO[DebtRepository]
REPO -->|SQLAlchemy| DB[(SQLite)]
FE([React Frontend]) -->|REST| API[FastAPI Routes]
API -->|CRUD| REPO
AG -->|response| BH
BH -->|reply| TG
API -->|JSON| FE
The OpenAI Agent is the brain. It receives natural language, decides which tools to call, handles disambiguation and clarification, and returns a human-friendly response. The REST API serves the React frontend directly (no agent involved).
memory-logger/
├── backend/
│ ├── main.py → FastAPI app + Telegram bot startup
│ ├── app/
│ │ ├── agent.py → Agent definition + system prompt
│ │ ├── tools.py → 7 @function_tool functions
│ │ ├── api/routes.py → REST endpoints for frontend
│ │ ├── bot/handler.py → Thin Telegram adapter (~30 lines)
│ │ ├── db/
│ │ │ ├── database.py → SQLAlchemy engine + session
│ │ │ ├── models.py → Debt and Transaction ORM models
│ │ │ └── repository.py → CRUD operations
│ │ ├── models/schemas.py → Pydantic request/response schemas
│ │ └── config.py → Settings from .env
│ └── pyproject.toml
├── frontend/
│ ├── src/
│ │ ├── App.jsx → Main app (stats, tabs, search, sort)
│ │ ├── api.js → Fetch wrappers for /debts endpoints
│ │ └── components/ → DebtCard, DebtList, AddDebtForm, etc.
│ └── vite.config.js
└── docs/
├── scenarios.md → Agent behavioral spec (50+ scenarios)
└── plans/ → Design and implementation docs
Agent (agent.py) — Single OpenAI Agent using gpt-4o-mini. System prompt covers Hinglish, currency parsing (5k → 5000), split math, disambiguation, and clarification rules.
Bot Handler (bot/handler.py) — ~30 lines. Receives a Telegram message, passes it to Runner.run(agent, message, session_id=user_id), sends back the response. No state machine, no slash commands.
Tools (tools.py) — 7 functions the agent can call (see below). Each tool interacts with the DB through DebtRepository.
REST API (api/routes.py) — 7 endpoints serving the React frontend. Direct CRUD, no agent involved.
Database (db/) — SQLite via SQLAlchemy. Two tables: debts and transactions (linked by foreign key).
| Tool | What it does |
|---|---|
create_debt |
Create a single debt (one-time or recurring) |
create_split_debts |
Create multiple debts for a group expense, split equally by headcount |
query_debts |
Search/filter debts by person, status, or direction |
edit_debt |
Update a debt's amount, note, person name, or monthly deduction |
record_payment |
Record a partial or full payment; auto-settles when remaining hits 0 |
settle_debt |
Mark a debt as fully settled |
delete_debt |
Permanently remove a debt |
All served at http://localhost:8000.
Create a new debt.
List debts, optionally filtered by status.
// Response — 200
[{ "id": 1, "person_name": "Rahul", ... }, { "id": 2, "person_name": "Priya", ... }]Get a single debt by ID.
Partial update. Recalculates remaining_amount when total_amount changes.
// Request (all fields optional)
{ "person_name": "Rahul K", "total_amount": 6000, "note": "Updated" }
// Response — 200
{ "id": 1, "remaining_amount": 4000, ... }// Response — 200
{ "detail": "Debt deleted" }Record a payment. Auto-settles if remaining hits 0.
// Request
{ "amount": 2000, "note": "Partial payment" }
// Response — 200
{ "id": 1, "remaining_amount": 3000, "status": "active", "transactions": [{ "amount": 2000, "paid_at": "...", "note": "Partial payment" }], ... }Mark fully settled. Records a final transaction for the remaining balance.
// Response — 200
{ "id": 1, "remaining_amount": 0, "status": "settled", ... }| Layer | Technology |
|---|---|
| AI Agent | OpenAI Agents SDK + gpt-4o-mini |
| API | FastAPI + Uvicorn |
| Database | SQLite via SQLAlchemy |
| Bot | python-telegram-bot |
| Frontend | React 19 + Vite 7 + Tailwind CSS 4 |
| Logging | Loguru |
- Python 3.10+, Node.js 18+
- OpenAI API key
- Telegram bot token
cd backend
# Create .env
cat <<EOF > .env
OPENAI_API_KEY=your_openai_key
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
EOF
# Install and run
uv sync
uv run python main.pycd frontend
npm install
npm run devDev server runs on http://localhost:5173 and proxies /debts to http://localhost:8000.