Expencify
A full-stack expense-management platform where employees submit multi-currency, receipt-backed expenses that flow through configurable multi-step approval chains. Companies self-provision on signup, seven role types get tailored dashboards, receipts are scanned via OCR, and amounts convert to a base currency in real time.
Context
Expense reimbursement is a small process that goes wrong in expensive ways: people hand-key data off paper receipts, currencies don’t reconcile, and approval routing — who signs off, in what order, by what rule — lives in email and spreadsheets.
Expencify centralises submission, digitises receipts with OCR, normalises currencies automatically, and encodes approval policy as data-driven rules rather than tribal knowledge.
My role, honestly
This was a four-person team, and I was one of the two heaviest contributors. I built the first working front end, then owned the pieces I care most about: role-based access control across seven roles, the executive-role data model, the rule-based approval engine, global currency formatting, the reusable email templating, and a repo-wide UI/UX pass. A teammate owned the OCR core and the initial backend scaffold; I integrated against both. I’m calling that out because “I built an expense app” would be a lie — “I built the approval engine and the front end of one” is the truth.
The approval engine
The part I’m proudest of is the rules. Approval policy is modelled relationally — ApprovalRule → ApprovalStep → ApprovalAction — so an admin can express sequential chains, percentage-based sign-off, a specific-approver auto-approve, a hybrid of those, or manager-first, each scoped by category and amount with its own priority.
An expense carries its own currentStepIndex through the chain, every action is an audit row with a comment, and the lifecycle (Pending → In Progress → Approved / Rejected) is derived from that trail rather than a status someone flips by hand.
Receipts & currency
Receipts upload to Cloudinary and run through Tesseract.js server-side, auto-filling merchant, amount, and date with a stored confidence score (that OCR core was a teammate’s work; I built the currency side of the same flow). Amounts are entered in any currency and converted to the company’s base via the ExchangeRate API, with country-to-currency selection wired in at signup through RestCountries — so a company is set up correctly before its first expense.
What I would change
The currency conversion calls an external API inline; I’d cache daily rates and convert against the cache, both for speed and to survive the API being down. And the approval rules deserve a proper builder UI — they’re powerful in the data model but still assembled more manually than an admin should have to.