[jm]Japheth Miller
← All projects
03
2024—now · live · onboarding founder partners
my SaaS

Otto — Utility billing, reconciled to the dollar

An end-to-end utility billing platform for multifamily property managers — vendor bill PDF in, branded resident invoice out, every charge auditable.

Next.js (App Router)TypeScriptSupabaseClaude APIStripe ConnectTailwind
Client
Ottomated, Inc. (self-funded)
Role
Founder · product, design, full-stack
Timeline
80+ build sessions · ongoing
Year
2024—now
Otto — Utility billing, reconciled to the dollar
ottomated-app
fig. 01 · otto — utility billing, reconciled to the dollar, in production.
app.ottomated.io / properties
fig. 02 · portfolio — properties grouped by state
fig. 02 · portfolio — properties grouped by state
app.ottomated.io / properties?q=
fig. 03 · portfolio search — filtering at scale
fig. 03 · portfolio search — filtering at scale

I designed and built Otto end-to-end — a multi-tenant SaaS that automates the full utility billing cycle for mid-market property managers and third-party billing companies. AI reads the vendor bill, the engine allocates costs across units, and every dollar is traceable in the audit trail. First real customer onboarded; three Founder Partner slots open.

The brief

Multifamily utility billing is still run on stitched-together spreadsheets and Conservice-tier portals that haven't shipped a real feature in a decade. Operators paste numbers between vendor PDFs, rent rolls, and Excel; residents get late, opaque invoices; nobody can show their math when a charge is challenged. I wanted one tool for the people running cycles themselves — every month, on real deadlines, with real residents asking real questions — that produced a dollar-reconciled invoice and an audit trail behind every line.

The cycle

A billing month moves through Otto along one canonical pipeline. Every screen, every API route, every report ladders to one of these seven steps.

01
Bills
Vendor PDF in. Claude OCR extracts charges, meters, periods.
02
Reads
Confirm meter reads. Anomalies flagged at submit.
03
Rent Roll
Verify occupants, sqft, move-ins, move-outs against the lease ledger.
04
Calculate
Engine allocates costs across units by method. Show-your-math derivation.
05
Approve
Cycle summary review, batch approval, recovery rate sanity check.
06
Ship
Branded invoice PDFs generated, emailed, posted to resident portal.
07
Close
Cycle locked. Audit log frozen. Writeoffs or balance forward applied.
fig. 04 · canonical pipeline — locked April 2026.
Constraints
  1. 01Self-funded — every feature had to earn its keep against the next paying customer.
  2. 02Multi-tenant from day one. Four roles: super admin, company admin, property manager, data entry. RLS at the row level on every table.
  3. 03Real money flows through Otto Pay. Stripe Connect Standard so funds go direct to the property — Otto never holds money.
  4. 04Real-world ingest: 18-page omnibus utility bills, scanned PDFs, no two vendors export the same way.
  5. 05State-by-state compliance — Florida and Texas at launch, Virginia shipping, more behind.
Approach
1.

Picked a stack chosen for boring reliability and AI-native ergonomics: Next.js App Router, Supabase (Postgres + Auth + Storage + RLS), Anthropic Claude for OCR, Stripe for billing, Resend for email, Vercel.

2.

Built the calculation engine first — pure functions for RUBS, submetered BTR/BTB, and fixed-fee allocation, audited line by line against real vendor bills before any UI was wired up.

3.

Modeled the entire workflow as one canonical pipeline — Bills → Reads → Rent Roll → Calculate → Approve → Ship → Close — and made every screen a view onto a step in that pipeline.

4.

Designed the system so every charge is derivable. 'Show your math' is the audit trail behind every cycle, every invoice, every dispute.

5.

Shipped Otto Resident (resident portal) and Otto Pay (Stripe Connect payments) as add-ons after the operator platform stabilized. First real paying customer onboarded against the production pipeline.

Calc engine

Seven allocation methods, audited on real data.

The engine is a set of pure functions for vendor-bill-to-resident-charge derivation. Recovery cap, common-area deduction, and proration apply across all methods. Every charge stores its inputs.

RUBS — Sqft
rubs_sqft

Proportional allocation by unit square footage. Vacant share absorbed by the property, not the resident.

RUBS — Occupant
rubs_occupant

Proportional allocation by occupant count, with the same vacant-share rule.

RUBS — Combo
rubs_combo

Weighted average of sqft and occupant. Configurable weights per property.

RUBS — Equal Share
rubs_equal

Equal split across all eligible units in the pool.

Submetered BTR
submetered_btr

Bill to Rates — vendor rate × unit reads. Each unit independent of every other.

Submetered BTB
submetered_btb

Bill to Bill — proportional allocation by unit submeter reads against the master meter.

Fixed Fee
fixed_fee

Flat per-unit or per-property charge. Recurring or one-shot final-bill trigger.

Hybrid per-property

Different methods per utility on the same property. Sewer always follows water.

Architecture

Boring stack on purpose. Every layer chosen so a single developer can keep the whole system in their head.

  • Framework
    Next.js (App Router) · TypeScript
    One process for SSR, RSC, and route handlers. No separate API server.
  • Database / Auth / Storage
    Supabase (Postgres + Auth + Storage)
    RLS at the row level. Auth, storage, and DB share one identity model.
  • AI / OCR
    Anthropic Claude (claude-sonnet-4-6)
    Vendor-bill OCR and bill-narrative generation. JSON-structured outputs.
  • Email
    Resend + react-email templates
    Transactional + invoice delivery. Every send logged to email_log with status.
  • Payments
    Stripe (subscriptions + Connect Standard)
    Otto bills the company; Otto Pay funds flow direct to property bank.
  • Styling
    Tailwind v4 + design tokens (Ember, Poppins, Lora)
    All visual decisions live in styles/globals.css. CSS vars only.
  • Hosting
    Vercel + edge middleware
    Edge rewrite for resident.ottomated.io subdomain. ISR for marketing pages.
What we shipped
Operator platform

Properties, units, leases, meters, rent roll, vendor bills — the full operational data model. Role-scoped, RLS-enforced, audit-logged.

AI bill ingestion

Drop a vendor PDF. Claude extracts charges, meter numbers, service addresses, and dates. Multi-page omnibus bills (18-page JEA, City of Houston) handled.

Calculation engine

Seven allocation methods — RUBS (sqft / occupant / combo / equal), submetered BTR + BTB, fixed fee. Recovery cap, CAD %, and proration audited on live data.

Cycle workflow

Bills → Reads → Rent Roll → Calculate → Approve → Ship → Close. Per-cycle review, batch approval, branded invoice PDFs generated and emailed automatically.

Otto Resident

Per-property resident portal at resident.ottomated.io/{slug}. Bill history, 'what changed' period comparison, consumption trends, white-label option.

Otto Pay

Stripe Connect Standard. ACH + card direct to the property's bank. Five-state payment-CTA gating; Otto never holds funds.

Compliance preview

State-aware compliance surface (Virginia first). Portfolio overview, rule-by-utility checklist, sources & updates feed.

Reporting & audit log

Pre-billing report, cycle close report, meter health, recovery trend. Append-only audit log on every mutation, abort-on-failure.

Iris · the AI operator

Otto is the platform. Iris is the worker inside it.

Iris drafts work — proposed reads, proposed allocations, proposed resident replies — and humans approve. Every action is logged to the audit trail with the prompt, evidence, and outcome attached.

Trust is earned in stages: v1 manual approve-each, v2 scheduled auto-approve within guardrails, v3 continuous within tighter guardrails. Trigger and trust are orthogonal.

Five-step approval loop
  1. 01
    IntentOperator or schedule asks Iris for a piece of work.
  2. 02
    EvidenceIris pulls grounded data — bills, reads, lease, prior cycles.
  3. 03
    ProposalIris drafts the action with a rationale and a confidence.
  4. 04
    GateHuman approves, edits, or rejects. Guardrails enforced server-side.
  5. 05
    Execute + AuditOtto applies the action. Audit row is INSERTed before the mutation runs.
Resident-side

Two add-on products sit on top of the operator platform. Both ride the same audit trail and the same charge derivation — the resident sees the same math the operator approved.

Otto Resident
add-on · v1.0

Per-property resident portal. Bill history, “what changed” period comparison, consumption trends, branded invoice email.

resident.ottomated.io / {property-slug}
  • Email + password resident accounts (PM invite or invoice CTA)
  • Submeter reads + Otto anomaly detection
  • Plain-language bill delta vs. prior cycle
  • White-label toggle (free) — property logo replaces Otto wordmark
  • 90-day post-lease portal access window
Otto Pay
add-on · v1.1

ACH + card payments via Stripe Connect Standard. Funds flow direct to the property’s bank account. Otto never holds money.

5-state CTA · connected · in review · connect · action needed · external
  • Stripe Connect Standard — Otto is not a money transmitter
  • Atomic dual-write (resident_transactions + payment_records) via SECURITY DEFINER RPC
  • Optimistic settlement — reversal + reconciliation tracked separately
  • Per-property activation, gated behind Otto Resident
  • PM-side refund, dispute, and resend-receipt actions
Outcome

What it did, in numbers.

7
step billing cycle, fully audited
7
allocation methods supported
167
Postgres migrations applied
99.5%
monthly uptime SLA
I stopped opening five tabs to figure out what my buildings did last week. That's the whole pitch.
First paying customer · Third-party utility billing company
What I’d do again
  • Build the calculation engine first, audit it on real bills, then wire the UI. The math is the product — every screen above it is window dressing.
  • Treat 'show your math' as a first-class feature, not an afterthought. The audit trail is the difference between trust and a phone call.
  • Lock the canonical pipeline early. Every screen, every API route, every report ladders to one of seven steps — the consistency compounds.
  • AI is for the boring parts (reading a 14-page utility bill) and the human is for the judgment (which lines are billable). Don't reverse them.
next project →
Ottomated.io — Marketing site for Otto
Have one of these?
Tell me about it.