Inspiration
Pre-revenue startups don't fail from one bad decision. They fail from a hundred small ones — silently, over weeks. Subscriptions that quietly auto-renew. Invoices that age past 60 days. Burn that creeps up because nobody noticed AWS spend doubled.
A real CFO costs €120,000 a year. Most early-stage founders run their finance side themselves on Sundays at midnight, in a panic, the week before runway hits a wall.
I built Aria — the CFO every founder needs but can't afford. Connected directly to bunq Business, watching every account in real time, taking real action behind a hard approval gate.
What it does
Aria is a multimodal AI agent for bunq Business with three surfaces and one agent loop:
- Voice — outbound phone calls via ElevenLabs + Twilio. Tap "Try Aria now" and your phone rings instantly.
- Vision — drop a receipt photo, Claude parses vendor / total / VAT, auto-matches to a bunq payment by amount + date + merchant heuristic.
- Text + Action — chat or dashboard. She watches runway, audits subscriptions, drafts dunning emails through Resend, posts colored Slack debriefs after every working session, and moves money through a bank-grade approval gate.
The differentiating capability is forward-looking pipeline reasoning. Most AI agents look at trailing burn. Aria asks about future events — a deal closing, a planned hire — records them with certainty weights, and recomputes runway:
$$\text{forward_runway} = \min { m \in \mathbb{N} : \; b_0 - m \cdot \beta + \sum_{e \in E_{\le m}} w_e \cdot a_e \; < \; 0 }$$
Where $b_0$ is current cash, $\beta$ is monthly burn, and each event $e$ has a signed amount $a_e$ and a certainty weight $w_e \in {1.0, 0.7, 0.4}$ for confirmed / likely / maybe. The simulation runs month by month for 24 months, interpolating fractional months when cash crosses zero.
This is the difference between "trailing runway 1.1 months" and "forward runway 8.8 months once your pipeline lands" — the latter is what a CFO actually says.
How I built it
Stack:
- Reasoning — Claude Sonnet 4.6 (text agent, vision, draft generation) and Claude Haiku 4.5 (voice agent inside ElevenLabs Conversational AI)
- Voice — ElevenLabs + Twilio outbound, with a webhook secret on every tool call
- Banking — bunq Business Sandbox, RSA-signed requests against
monetary-account-bank,payment,request-inquiryendpoints - Email — Resend (AI-drafted reminders that actually send)
- Notifications — Slack Incoming Webhooks + Block Kit attachments with color bars
- Web — Express + TypeScript + Zod, vanilla HTML/CSS/JS for frontend
- Storage — JSON files under
data/— no database
Architecture:
User → /chat or /dashboard or phone (Continuity-routed to Mac)
│
▼
Aria agent loop (multi-turn tool use, 21 tools)
│
├─ bunq sandbox (live RSA-signed reads + payments)
├─ Claude vision (receipt OCR)
├─ Resend (real email send)
├─ Slack (colored Block Kit attachments)
└─ Local JSON files (cashflows, actions, nudges, invoices)
The same agent — same 21 tools, same system prompt — runs across all surfaces. Voice is just a different transport.
Action gate. Every transfer requires:
- Aria calls
preview_transfer→ an HMAC-SHA256 signed token with 5-minute TTL - A pending entry surfaces an Approve / Reject popup on every page the user might be on (dashboard, chat, call), via a polling shared script
- Tapping Approve hits
/api/transfers/pending/:id/approvewhich validates the token and executes via bunq - Aria refuses to execute on verbal "yes" alone — she repeats "please tap Approve in your bunq app, I want a hard confirmation in the trusted UI"
This is the same pattern banks use — and I think it's the right pattern for any agent moving real money.
Challenges I ran into
ElevenLabs tool re-registration. Tools attached to an agent can't be deleted (HTTP 409). I had to detach via PATCH agent with tool_ids: [], then delete, then re-create, then re-patch. The script became:
detach → delete stale → create fresh → re-attach
Forward runway interpolation bug. First version computed cashAtStartOfMonth = cash + burn to interpolate the fractional month — wrong, because cash had already been mutated by both burn AND event inflows/outflows. Switched to capturing cashAtStart before mutation, computing the actual change, interpolating cleanly.
Cross-page approve popup. Initial version only mounted the modal on /dashboard. During phone calls I was stuck on /call and had to tab-switch to approve. Refactor: extracted into /aria-approve.css and /aria-approve.js — a self-mounting polling widget that injects markup, listens to /api/transfers/pending, fires browser notifications + Web Audio ding + tab title pulse when a popup opens. Now works on every page.
Verbal yes vs hard approval. Mid-build I realized verbal-only consent is a real risk — speech-to-text ambiguity, background noise, someone-in-the-room saying "yes". Updated both system prompts (text agent + voice agent registered in ElevenLabs) to forbid execution on verbal confirmation. Aria now refuses and redirects: "please tap Approve in your bunq app."
Accomplishments I'm proud of
- Real money moves. Not a mock. Real
payment_idfrom bunq sandbox, real RSA-signed request, hard confirmation gate. - Forward-runway reasoning that materially changes the CFO conversation. A grant arriving in 30 days isn't a footnote — it's the headline number, properly weighted.
- Multimodal continuity — the agent that gets your call also sees your dashboard, also reads your receipts. One agent, three surfaces.
- Real email and Slack. Aria-drafted dunning emails actually send through Resend. Block Kit debriefs hit a real Slack channel with color bars matching runway delta.
- Built solo in 17 hours, end-to-end working product, with Claude Code.
What I learned
- Approval UX matters more than model quality when an agent moves real money. The popup-tap pattern is what makes the demo trustworthy, not the LLM behind it.
- Forward-looking reasoning is the differentiator. Trailing-data summaries are commodity. Asking "what's coming?" and folding it into the math is what separates a CFO from a chatbot.
What's next for Aria
- Real bunq production OAuth instead of sandbox — partner integration so any bunq Business account can connect
- Native browser voice for stage demos — drop the phone dependency entirely with the ElevenLabs browser SDK
- Per-vendor reminder cadence — Aria learns which customers respond to which tone
- Stripe + Mercury + Wise connectors — multi-bank for founders who don't run everything through bunq
- Goal-driven proactivity — "keep my runway above 6 months" as a standing instruction, with Aria autonomously calling when that line is at risk
- Mobile app embedding — render the dashboard inside bunq Business as a native partner integration
Built With
- anthropic
- bunq
- claude
- claude-haiku
- claude-sonnet
- css
- elevenlabs
- express.js
- hmac
- html
- javascript
- json
- ngrok
- node.js
- resend
- rsa
- slack
- tsx
- twilio
- typescript
- zod
Log in or sign up for Devpost to join the conversation.