How an expense claim gets submitted
The approver can only check what it’s been given. So the first job is making it dead simple to hand it a claim, because the easier the submission, the fewer receipts end up lost in a coat pocket. There are three ways a claim gets in: somebody fills a short web form, somebody forwards a receipt email, or somebody drops a photo into a team chat. The point of having three is that people submit differently, and a claim that never gets submitted is the only one the system can’t help with.
Key takeaways
- Three intake lanes feed one claim record: a web form, a forwarded email, and a chat upload.
- Each receipt is read by Textract into amount, date, and vendor.
- Bedrock Haiku 4.5 sorts the receipt into a category so the claimant doesn’t have to be exact.
- The claimant sees the read-back values and can correct them before the claim is filed.
- However it arrived, every claim becomes one record in the same shape.
Three lanes into one claim
Lane 1: the web form
The simplest lane, and the one most people use. A short form sits behind a Lambda Function URL — no app to install, just a link the team bookmarks. Pick a category from a short list, type the amount, snap or upload a photo of the receipt, hit submit. The form Lambda writes the receipt image to s3://ea-receipts/ and creates a draft claim in DynamoDB with the claimant’s identity (they signed in), the category they picked, and a pointer to the receipt.
The form is deliberately tiny. Three fields and a photo. Anything the team has to think about is a thing that gets done wrong or skipped, so the form asks for the minimum and lets the read step in a moment fill in the rest.
Lane 2: forwarded receipt email
Half of business receipts arrive as email in the first place — the ride app, the SaaS invoice, the online order confirmation. Forcing the team to download those and re-upload them to a form is busywork. So there’s a dedicated inbound address — something like expenses@your-company.com — set up via Amazon SES. Forward the receipt email to it and the system takes over.
SES writes the raw email to s3://ea-raw-mime/. The S3 PUT triggers a parser Lambda that walks the email, finds the receipt (a PDF attachment, an image, or even the receipt text in the body), and starts the same read step the form uses. The claimant is matched from the “From” address of the forward, so the system knows whose claim it is. If the match is unclear, the claim is held and the system asks the sender to confirm before it goes anywhere.
Lane 3: chat upload
Some teams live in chat. The receipt photo gets dropped into a channel with a one-line “client lunch” before the person even thinks of it as an expense. Lane 3 catches those. A chat-intake Lambda listens for file uploads in a configured expenses channel, fetches the image, writes it to S3, and starts the read step. The person who posted is the claimant.
This lane is the most opt-in of the three. A team that doesn’t use chat for this loses nothing; a team that does gets to submit a claim without leaving the tool they’re already in.
Reading the receipt, once, the same way
Whatever lane a claim came in through, it hits the same read step. Amazon Textract reads the receipt image and returns the printed values — most importantly the total amount, the date, and the vendor name. Textract handles photos, PDFs, and scans natively, so a phone snap of a crumpled receipt works fine. Then a short Bedrock Haiku 4.5 call reads the vendor and line items and sorts the receipt into one of your policy categories: “this looks like meals,” “this looks like a taxi,” “this looks like software.” The category the claimant picked is treated as a hint, not the final word — if the receipt clearly says otherwise, the system flags the mismatch.
The claimant always sees the read-back before the claim is filed: “We read $36.00, today, Olive & Vine, category meals — correct?” They can fix any field with a tap. This matters because a misread amount is the one error you really don’t want flowing into an approval: too low and the claimant is shorted, too high and the business overpays. A human confirming the read keeps both honest.
Why everything becomes one record
Three lanes in, but only one shape of claim record the rest of the system ever sees. That’s deliberate. The checker in the next post, the routing after it, and the audit trail all work on a single claim shape: claimant, category, amount, date, vendor, receipt link, status. The lanes are just convenient doors; once a claim is through any of them, it’s indistinguishable from a claim that came through the others. One shape means one set of rules to reason about and one place to look when somebody asks “what happened to my claim?”
Next post: how the checker reads a claim, sorts it against policy, and picks one of four outcomes.
All posts