Finance & ops
Expense approvals
Build an expense approval flow your team actually uses: someone submits, an approver signs off, both get emailed, and a daily digest chases the pending pile.
Right now your expense approvals live in three places at once. A spreadsheet with a "status" column somebody updates by hand. A Slack thread where a receipt got screenshotted and then buried. An email chain that forks every time finance asks a follow-up question. Nobody can answer "what is waiting on me?" without scrolling, and the thing that was supposed to save time is the reason approvals sit for a week.
None of this is a hard problem. It is an internal tool you never had time to build, because building it the normal way meant standing up auth, a database, and email for what is basically a form and a button.
What it is on fuast
One capsule. A submitter files an expense, an approver approves or rejects it, both people get an email, and a scheduled job sends whoever owns approvals a daily list of what is still pending. It is a single small full-stack app, shared with your workspace like a doc, and every action is tied to the person who actually did it.
Steal this prompt
Paste this to your AI. It writes the capsule; you run fuast deploy.
Build an expense approval capsule on fuast. Employees submit an expense with an amount, a description, and an optional receipt file. An expense starts as "pending". Anyone in the workspace can view the queue; a designated approver can mark an expense "approved" or "rejected" with a note. Email the submitter when their expense is decided, and email the approver when a new one comes in. Add a scheduled job that emails the approver a digest of everything still pending at 9am UTC each weekday. Use fuast built-in auth, do not accept the submitter's identity as an argument.
What makes it real
The built-in auth is the whole point. The capsule
reads ctx.auth.userId for the person submitting, so nobody can file an expense as
someone else, and the approver check happens on the server where the client cannot
fake it. The database, file storage for receipts, transactional email, and the cron
for the digest all come from fuast/server. You provision none of it.
import { capsule, table, string, text, number, mutation, query, schedule } from "fuast/server";
export default capsule({
schema: {
expenses: table({
amount: number(),
description: text(),
submitterId: string(),
status: string(), // pending | approved | rejected
note: text(),
}).index("by_status", ["status"]),
},
mutations: {
submit: mutation(async (ctx, amount: number, description: string) => {
if (ctx.auth.isGuest) throw new Error("sign in first");
return ctx.db.expenses.insert({ amount, description, submitterId: ctx.auth.userId, status: "pending", note: "" });
}),
},
schedules: {
digest: schedule("0 9 * * 1-5", async (ctx) => {
const pending = await ctx.db.expenses.withIndex("by_status", (q) => q.eq("status", "pending")).collect();
await ctx.email.send({ to: "finance@you.com", subject: `${pending.length} expenses waiting`, text: "Open the app to review." });
}),
},
});
That is a working approval queue with real sign-in, real email, and a chase job, in one file. For the full walkthrough see build an approval app.
Stop updating a status column by hand. Build your approval flow.