VI
SaaS
Live

SPARK Finance — expense & debt management SaaS

Multi-tenant SaaS for tracking expenses, loans, overdrafts and partner lending: LLM-powered bank statement import, customizable dashboards, an AI assistant and a 28-tool MCP server that lets agents read and write financial data — one subdomain per tenant, isolated with PostgreSQL Row-Level Security.

Design & development (backend, frontend, infrastructure)

finance.devbyspark.com
SPARK Finance — expense & debt management SaaS

28

MCP tools

3

Active tenants

33 codes

Supported banks

300+

Automated tests

Background

Personal spending, a mortgage, overdrafts and money lent on to partners were scattered across statements from several banks and a few spreadsheets. Every month meant reconciling by hand: what had been paid, how much interest, how much partners still owed. SPARK Finance brings it all together — and was then split into a multi-tenant SaaS so others can use the same system.

Key features

  • AI statement import: accepts PDF, images, CSV and XLSX. The browser splits PDF pages (pdf.js) / converts XLSX to CSV and uploads files straight to Cloudflare R2 with presigned URLs; an LLM reads each page (~6 seconds/page) and produces a draft — nothing is written to the ledger until the user clicks Import. Duplicate imports are blocked by sha256, balances are checked for continuity across periods, and imports run in batches.
  • Transactions & categorization: categories, auto-categorization rules (preview, then apply), trends by category, monthly cash flow, income sources and fixed costs.
  • Loans & overdrafts: outstanding balances, estimated interest, overdraft draw/repay tracking and cash-flow reconciliation.
  • Partner lending: daily interest balance × rate/365 × days, multiple rate periods, manual adjustments, rebuilding from transactions. Partners sign in with a 6-digit code and report repayments via VietQR; the lender confirms through a Telegram bot.
  • Linked tenants: the lender invites the borrower; the borrower sees their own loans, and both sides reconcile loans and transfers automatically (±3 days tolerance).
  • Drag-and-drop dashboard: 9 widget types, plus chart widgets the AI assistant builds from a single prompt.
  • AI assistant: SSE chat via SPARK AI Gateway to a dedicated OpenClaw agent; the agent may only call read tools, and every write goes through a confirmation card.

Multi-tenant SaaS

  • One subdomain per tenant <tenant>.finance.devbyspark.com; the tenant is only ever determined from the subdomain, an authenticated API key or a server-signed session — never from headers, query strings or request bodies.
  • 4 layers of isolation: a dedicated tenant.ID type, a connection pool that refuses queries without a tenant, defaults + WITH CHECK from PostgreSQL Row-Level Security, and RLS USING clauses. The tenant travels in context.Context, so ~55 repository functions kept their signatures; 14 foreign keys moved to composite (tenant_id, …) keys.
  • Automatic tenant provisioning: a superadmin creates the tenant, the backend commits the subdomain's Ingress to the GitOps repo via the GitHub API, ArgoCD syncs and cert-manager issues TLS.
  • Roles: superadmin, tenant admin (6-digit code) and partner (separate access code); AI on/off and AI message quotas per tenant.

MCP for external agents

  • Endpoint https://<tenant>.finance.devbyspark.com/api/mcp — Streamable HTTP, JSON-RPC 2.0, with sessions.
  • 28 tools (24 read, 5 write; one semantic-search tool is hidden when embeddings are off): transactions, cash flow, categories, loans, overdrafts, partners, import drafts… — one registry shared by REST, MCP and the AI assistant.
  • spk_live_… API keys are stored only as SHA-256, with permissions chosen per tool and an expiry, valid only on the tenant's own subdomain, 120 calls/minute.
  • Sensitive operations (changing balances, recording lending periods) require confirm=true; writes carry an idempotency_key; every session and call is logged for 90 days. The "Connect MCP" screen generates ready-made commands/prompts for Claude Code, Claude Desktop, Cursor and OpenClaw.

Engineering

  • Backend: Go 1.22 with only 3 direct dependencies (pgx, minio-go for R2, x/crypto); the router is the standard library's http.ServeMux (~126 routes); a home-grown migrator with checksums and dry-run (37 migrations); distroless image.
  • Database: PostgreSQL with Row-Level Security, pg_trgm for search, pgvector for the assistant's semantic memory.
  • Frontend: React 18, Vite, TypeScript, MUI + MUI X, ApexCharts, react-grid-layout, pdf.js, SheetJS.
  • Operations: GitHub Actions → Docker Hub → GitOps repo → ArgoCD on Kubernetes (K3s), cert-manager + Let's Encrypt, zero-downtime rolling updates.
  • Testing: 300+ Go tests, each running in its own Postgres schema; tests that lock in the money logic (reproducing all 12 real lending periods); 31 multi-tenant end-to-end scenarios over HTTP with each tenant's Host header.

Results

  • Replaced spreadsheets with a self-reconciling financial ledger: lending figures match the original spreadsheet on 12/12 periods.
  • Running for 3 tenants on finance.devbyspark.com and their own subdomains.
  • AI agents (Claude, OpenClaw…) read and write financial data over MCP with per-tool permissions and a safe confirmation step.

Highlights

  • Moved from a single-owner app to a multi-tenant SaaS without changing the signatures of ~55 repository functions: the tenant travels in the context, with 4 layers of isolation backed by PostgreSQL Row-Level Security (22 RLS test cases).

  • LLM statement import: PDF/image/CSV/XLSX uploaded straight to R2 via presigned URLs, page-by-page OCR (~6 s/page), sha256 de-duplication, running balance checks, and nothing written until the user clicks Import.

  • Streamable HTTP MCP server per tenant: spk_live keys with per-tool permissions and expiry, mandatory confirmation before changing balances, 90-day logs; one tool registry shared by REST, MCP and the AI assistant.

  • Partner lending: daily interest, multiple rate periods, partners report repayments via VietQR and the owner confirms via a Telegram bot; matches the real spreadsheet on 12/12 periods.

  • Links lender and borrower tenants with two-way reconciliation of loans and transfers.

  • Adding a tenant generates its Ingress in the GitOps repo via the GitHub API; ArgoCD syncs and issues TLS for the subdomain.

Gallery

Customizable dashboard: monthly cash flow, loans & overdrafts (amounts hidden)
SPARK Finance — expense & debt management SaaS — a project by Vũ Xuân Bình