An open-source AI-native personal finance help desk. Connect your Gmail, and PocketPilot automatically reads bank transaction emails, parses them into structured data, and gives you a AI-powered financial assistant that knows your actual numbers.
Gmail → Parse → Supabase → Chat UI (Expo)
- Auto-imports transactions from any bank email (HDFC, ICICI, SBI, Axis, DCB, Kotak, and more)
- Multi-Gmail support — connect more than one Gmail account, sync them all
- AI chat — ask "how much did I spend in Goa?" and get a real answer from your real data
- Background agents — anomaly detector runs daily, weekly digest on Sunday, monthly review on the 1st
- Expo mobile app — Dashboard, Chat, Agents, Transactions, Settings tabs
personal-finance-agent/
├── backend/ # FastAPI (Python)
│ ├── routes/
│ │ ├── gmail.py # OAuth + email sync + parsing
│ │ ├── chat.py # Claude-powered agentic chat
│ │ ├── analytics.py # Summary, trends, insights
│ │ └── transactions.py
│ ├── agents/
│ │ ├── anomaly.py # Flags unusual transactions
│ │ ├── weekly_digest.py
│ │ └── monthly_review.py
│ ├── tools/
│ │ └── finance_tools.py # 12 tools Claude can call
│ ├── jobs.py # APScheduler cron runners
│ └── main.py
├── apps/mobile/ # Expo React Native app
│ ├── app/(tabs)/ # Dashboard, Chat, Agents, Transactions, Settings
│ └── lib/ # API client, theme
└── supabase/
└── schema.sql # Full DB schema
| Layer | Tech |
|---|---|
| Backend | Python 3.13, FastAPI, Uvicorn |
| AI | Anthropic Claude (Sonnet for parsing, Haiku for chat) |
| Database | Supabase (PostgreSQL + Auth) |
| Gmail API (OAuth 2.0) | |
| Mobile | Expo 52, React Native, Expo Router |
| Deploy | Railway (backend) |
- Python 3.11+
- Node.js 18+
- A Supabase project (free tier works)
- An Anthropic API key
- A Google Cloud project with Gmail API enabled
git clone https://github.com/your-username/personal-finance-agent
cd personal-finance-agent/backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtOpen your Supabase project → SQL Editor → paste and run supabase/schema.sql.
cp .env.example .envEdit .env:
ANTHROPIC_API_KEY=sk-ant-...
SUPABASE_URL=https://xxxxxxxxxxxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJ...
GMAIL_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com
GMAIL_CLIENT_SECRET=GOCSPX-...
GMAIL_REDIRECT_URI=http://localhost:8000/gmail/callback- Go to Google Cloud Console → Create project
- Enable Gmail API
- Create OAuth 2.0 credentials (Web application type)
- Add
http://localhost:8000/gmail/callbackas an authorised redirect URI - Download the client ID and secret into your
.env
python main.py
# API at http://localhost:8000
# Docs at http://localhost:8000/docs# Get the OAuth URL
curl http://localhost:8000/gmail/auth?user_id=YOUR_USER_ID
# Open that URL in a browser, authorise Gmail access
# Then trigger a sync
curl -X POST http://localhost:8000/gmail/sync/YOUR_USER_IDcd apps/mobile
npm install
cp .env.example .envEdit .env:
EXPO_PUBLIC_API_URL=http://localhost:8000
EXPO_PUBLIC_USER_ID=your-supabase-user-uuid-hereFind your user UUID in Supabase → Authentication → Users after signing up.
Run:
npx expo start
# Press i for iOS simulator
# Press a for Android emulator
# Scan QR code with Expo Go on your phoneParsing uses a two-stage approach:
- Universal regex — fast, covers ~90% of Indian bank formats (HDFC, ICICI, SBI, Axis, Kotak, DCB, etc.) using amount + debit/credit keyword patterns rather than per-bank templates
- Claude Sonnet fallback — for anything regex misses; more accurate than Haiku for this task
Emails are filtered before parsing:
- Skips OTP, newsletter, and marketing emails
- Requires a transaction signal in the subject line
- Skips personal domains (gmail.com, yahoo.com)
Three agents run on a schedule (via APScheduler):
| Agent | Schedule | What it does |
|---|---|---|
| Anomaly detector | Every new transaction | Flags amounts >2× merchant average |
| Weekly digest | Sunday 9am IST | Summarises the week's spending |
| Monthly review | 1st of month, 8am IST | Reviews the previous month |
Insights are stored in agent_insights and surfaced in the Agents tab.
Claude has 12 tools it can call during a chat:
| Tool | Description |
|---|---|
get_monthly_summary |
Income, spending, category breakdown for a month |
get_transactions |
Raw transactions filtered by date, category, merchant |
summarize_date_range |
Totals for any date range (trips, events) |
get_category_trend |
Category spend over N months |
get_merchant_breakdown |
All transactions at a merchant |
get_top_merchants |
Top spenders for a month |
compare_months |
Month-on-month comparison |
get_recurring_charges |
Subscription and recurring payment detection |
get_budget_status |
Spend vs budget limits |
get_goals |
Savings goals and on-track status |
get_unusual_transactions |
Statistical outliers |
calculate |
Safe arithmetic — model never does mental math |
GET /health
GET /gmail/auth?user_id= → OAuth URL
GET /gmail/callback → OAuth callback (set as redirect URI)
POST /gmail/sync/:user_id → Sync all connected Gmail accounts
GET /gmail/status/:user_id → List accounts + last sync times
GET /gmail/skipped/:user_id → Emails that were skipped
GET /analytics/summary/:user_id → Monthly summary
GET /analytics/trends/:user_id → Spending trends
GET /analytics/insights/:user_id → Agent insights
POST /chat/ → Chat message
GET /transactions/:user_id → List transactions
POST /transactions/ → Create transaction
Full interactive docs at http://localhost:8000/docs when running locally.
| Service | Why |
|---|---|
| Railway | Zero-config Python deploys from a GitHub repo. Detects FastAPI automatically, handles HTTPS, and has a generous free trial. No Dockerfile needed. |
| Supabase | Managed Postgres with a free tier that comfortably fits personal finance data. Built-in auth, row-level security, and a dashboard for inspecting data. |
| EAS Build (Expo) | Builds Android APKs and iOS IPAs in the cloud without needing Xcode or Android Studio locally. Free tier covers ~30 builds/month. |
| Anthropic Claude | Pay-per-token. Haiku (used for chat) is cheap enough for personal use; Sonnet (used for email parsing) is called only when regex fails. |
Railway deploys directly from a GitHub repo. If you haven't pushed yet:
git remote add origin https://github.com/your-username/pocketpilot.git
git push -u origin main- Go to railway.app and sign in with GitHub
- Click New Project → Deploy from GitHub repo
- Select your repository
- When the first deploy fails, click Settings → Source → Root Directory and set it to
backend - Redeploy — Railway will detect Python + FastAPI automatically via Railpack
Settings → Networking → Generate Domain. You'll get a URL like your-app.up.railway.app.
Go to Variables tab and add:
ANTHROPIC_API_KEY = sk-ant-...
SUPABASE_URL = https://xxxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY = eyJ...
GMAIL_CLIENT_ID = xxxx.apps.googleusercontent.com
GMAIL_CLIENT_SECRET = GOCSPX-...
GMAIL_REDIRECT_URI = https://your-app.up.railway.app/gmail/callback
Railway redeploys automatically when you save variables.
Add your Railway URL as an authorised redirect URI:
- Google Cloud Console → Credentials → your OAuth client
- Add
https://your-app.up.railway.app/gmail/callbackto Authorised redirect URIs
Verify the deployment:
curl https://your-app.up.railway.app/health
# → {"status":"ok"}npm install -g eas-cli
eas login # creates a free Expo account if you don't have oneThe mobile app's .env is gitignored and never reaches the build server. Set the variables on EAS directly:
cd apps/mobile
eas env:create --scope project --name EXPO_PUBLIC_API_URL \
--value "https://your-app.up.railway.app" \
--visibility plaintext --environment preview
eas env:create --scope project --name EXPO_PUBLIC_USER_ID \
--value "your-supabase-user-uuid" \
--visibility plaintext --environment previewFind your user UUID in Supabase → Authentication → Users.
eas build -p android --profile previewThis uploads your code to Expo's build servers (~5-10 min). When done, you get a download link. Install the APK directly on your Android phone.
| Service | Free tier | Paid |
|---|---|---|
| Railway | $5 trial credit, then ~$5/month (Hobby) | Scales with usage |
| Supabase | Free tier: 500MB DB, 2GB bandwidth | $25/month (Pro) |
| EAS Build | 30 builds/month free | $99/month (Production) |
| Anthropic | Pay per token | ~$0.80 per 1M Haiku tokens |
Realistic monthly cost for personal use: $5–10/month (Railway Hobby + Anthropic API usage)
Supabase free tier and EAS free tier are sufficient for personal use.
- Railway: The backend sleeps after inactivity on the free tier — first request after sleep takes ~15s. Acceptable for personal use. To avoid it, upgrade to Hobby ($5/mo).
- Anthropic: Most chat queries use 2-4 Haiku API calls. At $0.80/M input tokens, 1000 chat messages costs < $1. Email parsing uses Sonnet only as a fallback — if regex covers your banks, Sonnet calls are rare.
- Supabase: A year of personal transaction data is well under 100MB. Free tier has no practical limit for this use case.
- EAS Build: After initial setup you rarely rebuild. 30 free builds/month is more than enough.
Railway doesn't detect the Python app
Root cause: Railway scans the repo root and finds no Python files. Fix: set the root directory to backend in Railway → Settings → Source.
Gmail OAuth redirect fails after deploy
The GMAIL_REDIRECT_URI must exactly match what's registered in Google Cloud Console. After getting your Railway URL, update both the Railway variable and the Google Cloud authorised redirect URI list.
App shows blank screen on first install Two causes: (1) fonts fail to load from Google Fonts on slow connections — the app has a 3s timeout and falls back to system fonts; (2) the backend is cold-starting on Railway — the first request after a long idle period can take 15-20s.
"Network request failed" on Android
React Native 0.76's New Architecture fetch has compatibility issues with TLS 1.3 + HTTP/2 (Railway's stack). The app uses XMLHttpRequest instead which uses Android's older HTTP stack and works reliably.
EAS build fails with missing assets
The assets/ folder (app icon, splash screen) must be committed to git. EAS builds from the repo, not your local filesystem.
EAS environment variables not baked into the APK
Variables in .env are gitignored and invisible to EAS. Always set them with eas env:create and verify with eas env:list --environment preview before building.
APK installs but all pages show errors
The API URL (EXPO_PUBLIC_API_URL) is baked in at build time. If you rebuild after changing the Railway URL, reinstall — old APKs continue pointing to the old URL.
Pull requests are welcome. For major changes, open an issue first.
Areas that could use help:
- Web dashboard (Next.js)
- Budget management UI
- More AI agents
- Cost opimisation
- Equity and investment tracking.
GPL
