Vibe code your own Expensify

Vibe code your own Expensify as a receipt photo, typed fields, and a CSV. The card is $5 to $9 a month. OCR, card feeds, and payouts are the long row.

  • teardown
  • vibe-coding
  • scope

You can vibe code your own Expensify as a receipt photo, typed fields, and a CSV. Expensify is that product. The catalog line is “Submit receipts, approve totals, export for accounting.”

The card lists $5–$9 a person a month and does not name the plans. It scales with the team. The first estimate is 12 hours. The verdict is “For simple reimbursements”.

Last time I wrote about vibe coding your own Postman. That was a regular backlog post. This one is a teardown.

The public sources are Daniel Vidal’s November 28, 2023 post on receipt accuracy, the June 2, 2021 TechCrunch EC-1 on the engineering stack, and Expensify’s own tech stack page. Where those pages stop, I call it a guess.

Vibe code your own Expensify from a receipt

The Expensify page splits the work into three rows.

The first row is 12 hours. Receipt upload, an expense entry, and a CSV export. You photograph the paper, you type the merchant and the amount, and you download a file the accountant can open.

The second row is 1 week. Reports, an approval workflow, more than one currency, and email notifications. A manager’s approve button can wait until the CSV already matches the receipts.

The third row is 3 months and up. Receipt OCR, corporate cards, and moving actual money. The table labels that row as the part you should skip.

If you tell an agent to “clone Expensify”, it aims at the third row. Copy the prompt, then pin it to the first one.

Store Employee, Expense, Receipt, ExpenseReport, and ApprovalEvent. The card names all five. For these 12 hours, Employee, Expense, and Receipt are enough. The report and the approval event arrive with the week row.

An employee is the person who spent the money. An expense is the merchant, the date, the amount, the currency, the category, and a note. A receipt is the photo in object storage, tied to that expense. An expense report is a bundle of expenses with a status. An approval event is the comment, the approve, or the return, and it is append-only.

The guardrail is on the card. Use private signed receipt URLs. Keep approval history immutable. Handle currency explicitly.

The audience is a small company with one reimbursement policy. The goal on the card is to collect the evidence, route a report, and export the approved data. The 12-hour slice stops at the evidence and the export. Approval is the next row.

An invoice you send a client is FreshBooks at $23–$60 a month, in Vibe code your own FreshBooks. A household budget is YNAB at $9.08–$14.99 a month, in Build a YNAB alternative. Who owes whom after a trip is Splitwise at $3.33–$4.99 a month, in Vibe code your own Splitwise. Those move different money. This build records a receipt Ada already paid, and it stops before anyone is paid back.

How does Expensify work

SmartScan is the part people mean when they say the app “reads” a receipt. Vidal’s post says even the best OCR gets a real-world receipt right around 85% of the time, and that their fuller stack is what they count on for about 99%. Those figures are from that post.

He lists the pieces. A phone flow built around the photo. OCR, including their own, after what he calls millions of receipts. Parsers for layouts they see constantly. He names emailed receipts plus Home Depot, Amazon, and Delta. A human check when the system is not confident. He describes thousands of reviewers, and a pile-up at month end. Then a match from the receipt to a personal or company card feed, and a flag when a person edits a scanned field.

The 2021 TechCrunch piece quotes CEO David Barrett on the same scan. He describes a “fire and forget” screen. You photograph the receipt, you put the phone away, and the fields fill in later. Humans step in when the read is not confident. After a 2017 beta sent receipts to Amazon Mechanical Turk, a mistake TechCrunch says touched up to 208 receipts, they stopped using that platform. The reviewers in that piece are contractors under NDAs. The queue is ExpensiWorks. COO Anu Muralidharan said it sits on the same database and the same expense records.

That database is Bedrock. TechCrunch describes it as their relational store on top of SQLite, built to replicate across slow links between data centers. The shape they describe is three data centers, redundant inside each, and a Paxos-style consensus so a failed node can pick a new leader. Barrett told them the useful property is one conceptual database, so a report can reach a boss at another company without a partition between customers. They open-sourced it in 2016. I have not seen a newer post that redraws this. Their tech stack page still names Bedrock as the core: C++ for the database, PHP for the web API, React on the web, and mobile in JS, Swift, and Java. Ubuntu, hybrid hosting with the emphasis on bare metal, and 2.5M in physical hardware in 2019, by their count.

Barrett also told TechCrunch they publish features rather than papers, so Concierge’s matching rules are not public. The 12-hour clone stores a photo and the fields a person typed. SmartScan, the reviewer queue, the card feed, and Bedrock are the month row.

Screens of an Expensify alternative

v1 needs the expense form and the export list. A report with draft, submitted, approved, and reimbursed states is the week row. A scan that fills the amount for you is the month row.

New expense

The form is the photo, then the fields. Ada’s receipt is Blue Bottle on November 4. She types the merchant, the date, $18.40, USD, category Meals, and a note. Save stores the image privately and the row beside it. The amount is integer cents, 1840, so a later total cannot drift by a floating-point cent.

WIREFRAME · NEW EXPENSE Receipt photo Blue Bottle Nov 4 · Meals 18.40 USD · typed Note · Ada and Cam Stored as 1840 cents Save
The photo is evidence. The numbers are what Ada typed. Nothing on this screen reads the image.

The list

The list is one row per expense. Date, merchant, category, amount, and a receipt link that goes through the app. Export writes the CSV of the rows she can see. A manager queue is not on this screen yet.

WIREFRAME · EXPENSES Nov 4 · Blue Bottle · Meals 18.40 USD · receipt link Export CSV
The CSV is the handoff. The receipt link expires, and it is not a public file URL.

How an expense gets exported

Ada opens the form on her phone, adds the photo, and types the five fields. Save writes the object to R2 and the expense to D1 in one go. If the row fails, the photo should not sit there unlabeled. If the photo fails, do not keep an expense that claims a receipt you cannot open.

She exports the month. The CSV has the date, the merchant, the category, the cents, the currency, and a link. The link is minted when someone opens it, and it dies. A guessed object key returns nothing.

Your browser talks to one Astro app. Node.js plus Astro is the default. Use TanStack Start if React is your weekday language. The capability line on the card is Astro, R2, and D1. The photo is the only object. The tables are employee, expense, and receipt.

On the week row she bundles that expense into a report, submits it, and her manager approves or sends it back with a comment. Each of those is a new approval event. Editing the expense afterward does not rewrite the event. Email on submit and on return is the same row. A second currency is too, and the guardrail wants the code stored on the expense, not assumed.

ADA TYPES THE RECEIPT Photo, Blue Bottle, 1840 cents ASTRO WRITES BOTH No orphan photo, no empty claim R2 AND D1 Image private. Row is the numbers. CSV FOR THE ACCOUNTANT Date, merchant, cents, signed link
Twelve hours ends at a file someone else can import. Paying Ada back is a later row.
ADA'S PHONE Photo plus the fields she typed ASTRO APP ON THE REQUEST Cents, one currency, owner scope R2 PHOTO, D1 ROW Link is signed and short-lived LATER, THEN SKIP Reports, approval, more currencies OCR, reviewers, card feeds Bedrock, cards, and payouts
The photo and the typed row are the whole v1. The scan pipeline and the bank sit in the dashed box.

The hard half of an Expensify alternative

The 12-hour expense is a photo you can open and numbers you typed. The CSV adds up because every amount is cents in one currency. That is checkable on one laptop.

SmartScan is the month row because the misses are the work. Vidal says a crumpled receipt or a handwritten tip is where OCR goes wrong, and one bad total is enough that people stop trusting the scan and type everything. Parsers cover vendors they see constantly. The human queue is how they claim the rest, and he points at month-end volume as the staffing problem. A vision API that writes JSON into the amount field will look finished on a clean emailed receipt and miss the pocket one. The non-goal says not to scan receipts with AI.

Card feeds sit on that same row. Matching a receipt to a transaction needs the bank file first. Their post treats the match as part of scanning, after the fields exist. A payout that reimburses Ada is a transfer. The non-goal says not to move money, issue cards, calculate tax, or replace the accounting tool. FreshBooks is where an invoice lives. This list is the receipt she already paid.

Bedrock is the other long piece. Three sites, consensus, one logical database, machines they bought. The 2021 account and the stack page do not name today’s cluster, so I will not guess it. D1 holds Ada’s receipts. Bedrock is the system they describe for every customer at once.

The week row is the bridge. A report status, an append-only approval, an email, and a second currency code. Fixing a typo adds a fact. It does not edit the old approval event.

12-HOUR V1 A private photo of the receipt Merchant, date, cents, category One currency on the row A CSV with a signed link HARD HALF OCR, parsers, and a review queue Month-end volume and card feeds Bedrock across three sites Cards, tax, and a real payout
Twelve hours is evidence and a file. The scan network and the payment are the expense platform.

The scope post is the same three-row habit. The prompt post is where you paste the signed-URL rule so the agent does not leave the bucket public “until auth is ready”.

Start with the first row

Go to Build your own Expensify. Copy the Astro prompt, or TanStack Start if that is what you write.

Tell the agent the first estimate only. Receipt upload, an expense entry, and a CSV export. Twelve hours.

Save the Blue Bottle receipt, export the CSV, and open the receipt link in a private window after it expires. The file should show 1840 cents, and the expired link should fail. If you need the photo to fill the amount in, a card feed, or a payout, keep the $5 plan for that part.

Keep reading

Read all articles →