How to

How to build an expense policy checker

Nobody reads the expense policy, and the people who enforce it are checking receipts by hand. Here is the model behind a checker that reads the claim, the prompts to build it, and what it takes to run it.

The short answer

An expense policy checker is four parts: the policy expressed as rules rather than prose, a claim structure to evaluate them against, a result that explains itself, and a review path for the ambiguous cases. The mistake is trying to automate the decision. What you want to automate is the reading: a reviewer should see "this dinner is 40 over the limit for this city and has no attendee list" rather than a receipt and a policy PDF. Build it as an explainer, not a judge.

expense-checker.helix-app.ai

Expense checker

11

Waiting on you

183

Cleared without review

6

Exceptions this month

2

Rules exceptioned repeatedly

Category

Dinner, client

City

London

Amount

98.00

Receipt

Attached

Dinner limit for London is 60

Over by 38

Needs review, not a blocker

Attendee list

Missing

Blocker. Client dinners need who was there.

A sketch of the pre-submission check, which is where most policy breaches get fixed. The two findings are written as sentences with the gap in them, so nobody opens the policy document.

What matters here

  • An expense policy checker is four parts: rules, a claim structure, an explained result, and a review path for the ambiguous cases.
  • The value is in reading, not deciding. Show the reviewer which rule the claim touched and by how much, and let them make the call.
  • Rules belong in one editable file. Limits change by city, season and seniority, and a policy encoded across the codebase stops being maintainable in a month.
  • Every rule needs a severity. A missing receipt is a blocker, a claim two currency units over a meal limit is a note, and treating them the same trains people to ignore both.
  • Record the policy version each claim was checked against. When a limit changes, old claims must not silently start failing.
  • Expense data is personal data, so scoped access and a named owner matter as much as the rules themselves.

Who this is for

You own the expense policy, or you approve claims against one. You already know your limits and your categories. What you want is for the obviously fine claims to stop consuming review time, and for the exceptions to arrive already explained.

How it works in practice

What happens to a claim, and why most of them never reach a reviewer.

  1. 1

    Check before you submit

    A claimant can test a draft claim against the policy and see exactly what it would flag. Most breaches are people not knowing the limit.

  2. 2

    The claim is read, not judged

    Every applicable rule runs against it and produces a sentence: what it checked, what it found, and the gap.

  3. 3

    Clear claims disappear

    Anything with no findings is approved and never enters the queue. If everything needs review, the tool is re-approving what was already fine.

  4. 4

    The rest arrive already explained

    A reviewer sees the claim, the failing rules in plain language, the receipt and the claimant's recent history, on one screen.

  5. 5

    Exceptions get a reason

    Approving outside policy requires a reason from a short list. Those reasons become the report that tells you which rule is wrong.

  6. 6

    The policy changes with evidence

    A rule exceptioned every week is a rule that needs rewriting, and now you can prove it.

What a policy checker is made of

A policy checker is not an approval bot. It is four parts, and the third one is what decides whether people trust it.

Rules

Your policy as data: limits per category, per city, per role, receipt thresholds, required fields, banned categories, and the date ranges each version applies to. One file, edited by the policy owner.

A claim structure

What the checker evaluates: claimant, date, category, amount, currency, merchant, attendees, project or cost centre, receipt attached or not.

An explained result

Not pass or fail. Per rule: what it checked, what it found, and the gap. A reviewer should be able to read the result and never open the policy document.

A review path

Where the ambiguous ones go, with an exception mechanism that records who approved and why. Exceptions are data, and the pattern in them is usually a sign the policy needs changing.

These prompts start from a Helix project, which is what handles sign-in, credentials and hosting. Start with Helix

The prompts

Paste these into Claude Code, Codex or Cursor in order. Each one leaves you with something that runs, so you can stop after any step.

  1. The policy, as something you can edit

    Rules in one file, claims in another, and nothing hardcoded.

    In this Helix project, build me an expense policy checker.
    
    The most important design decision comes first: the policy lives in one
    file that I can edit, not scattered through the code. Limits change by
    city, by category, by seniority and by season, and a policy encoded across
    a codebase stops being maintainable within a month.
    
    So: a rules file with per-category limits, per-city overrides, the receipt
    threshold, which fields each category requires, banned categories, and the
    date each version takes effect. Load it at runtime so changing a limit is
    not a code change.
    
    Then the claims themselves: claimant, date, category, merchant, amount,
    currency, project, attendees, and whether a receipt is attached. A form to
    submit one, and a list.
    
    I sign in through Helix, so do not build a login page. Read the signed-in
    user from the runtime. And do not put any key or connection string in the
    code: if this needs to reach another system, tell me and I will create the
    connection in my workspace.
  2. Read the claim, do not judge it

    The output that makes a reviewer faster instead of a second gatekeeper.

    Now run the rules, and be careful about what you produce.
    
    Do not give me a pass or fail. For each rule that applies, tell me what it
    checked, what it found, and the gap: "dinner limit for London is 60, this
    claim is 98, over by 38". Write it in plain language. A reviewer should
    never need to open the policy document.
    
    Give each rule a severity, set in the rules file rather than in code. A
    missing receipt is a blocker. Two units over a meal limit is a note.
    Treating those the same trains people to ignore both.
    
    The claim's overall status is derived from its findings, never entered by
    hand. And record which version of the policy each claim was checked
    against, so raising a limit next quarter does not retroactively change the
    history of claims that were assessed correctly at the time.
  3. The queue, and the exceptions report

    Only the ambiguous claims, and what the arguments about them add up to.

    Build the reviewer's queue and put only the ambiguous claims in it.
    
    Clear claims go straight through and never appear. Each item that does
    appear shows the claim, the failing rules with their explanations, the
    receipt, and what this person has claimed recently.
    
    Actions: approve, approve as an exception with a required reason, reject
    with a reason, or send back for more information.
    
    Then the report I actually want: every exception granted, by rule, by
    approver, over time. A rule that gets exceptioned constantly is a rule
    that does not match how the company works, and this turns that from an
    argument into a number.

    Worth knowing. The exceptions report is the part nobody asks for and everybody ends up using. It converts policy debates into evidence.

  4. Stop the claim before it is submitted

    The change that shrinks the queue instead of processing it faster.

    Last, let a claimant check a draft claim against the policy before
    submitting, with exactly the same explanations a reviewer would see.
    
    Most policy breaches are people not knowing the limit rather than people
    pushing their luck. Every claim fixed here is a claim that never enters
    the queue at all.
    
    Then two small reports: spend by category and team over time, so I can see
    whether the policy is drifting away from what things actually cost, and
    the people who hit the same rule repeatedly. Read that second one as a
    training need, not an enforcement list.
  5. Connect it to real systems

    The step that turns a working prototype into something with your data in it.

    Now connect it to the systems we actually run, so it works on real data
    rather than the rows you seeded.
    
    Anything in angle brackets is a placeholder. Swap it for whatever we use
    instead, and have the app name the connection it wants rather than assume
    a vendor, so changing my mind later is a config change and not a rewrite.
    
    <Expensify>. Read submitted claims and write back the check result, so
    reviewers work in one place.
    
    <Brex>. Match claims to card transactions, which catches the duplicate and
    the missing receipt.
    
    <Okta>. Resolve the claimant and their manager, so a claim routes to
    whoever actually manages them today.
    
    <Slack>. Send the pre-submission result to the claimant, which is where
    most breaches get fixed.
    
    <NetSuite>. Post approved claims for reimbursement once the check clears,
    so nobody rekeys them.
    
    <Google Sheets>. Let the policy owner edit limits in a sheet, if that is
    genuinely easier than a form.
    
    Only write where I have said to write. Everything else is read only.
    
    Do not write a key, a token or a connection string anywhere in the code,
    and do not ask me to paste one. Tell me which connections you need and I
    will create each as an authentication in my Helix workspace. Reference
    them by alias.

    Worth knowing. Every name in angle brackets is a placeholder for whatever you run. Helix holds the credential as a workspace authentication, so the app references an alias and never the secret itself.

  6. Ship it

    The last step of every build: a URL, and the right people on it.

    Deploy my app.

    Worth knowing. Your assistant runs helix deploy underneath and the app comes back as a URL. Expense claims are personal data, so set Access Control before sharing: claimants see their own, reviewers see their team's.

What it connects to

The policy is yours to write. The claims and the receipts are already somewhere, and the checker should read them there.

  • Expensify

    Read submitted claims and write back the check result, so reviewers work in one place.

  • Brex

    Match claims to card transactions, which catches the duplicate and the missing receipt.

  • Okta

    Resolve the claimant and their manager, so a claim routes to whoever actually manages them today.

  • Slack

    Send the pre-submission result to the claimant, which is where most breaches get fixed.

  • NetSuite

    Post approved claims for reimbursement once the check clears, so nobody rekeys them.

  • Google Sheets

    Let the policy owner edit limits in a sheet, if that is genuinely easier than a form.

Each line says whether the app reads, writes or both. Anything not described as writing should be read only.

Named systems are the ones most teams actually run, not a list of the only ones that work. Swap in whatever you use.

Each connection is an authentication in your Helix workspace, referenced by alias. The app names the connection it wants and never holds the credential, so nothing here ends up in your code and any of it can be rotated or revoked without a redeploy.

Running it for real

The build gets you a working checker. It holds personal spending data and influences who gets paid back, so it needs to be run properly rather than shared as a link.

AI Deployment

Get it to a URL people can open

One command takes the app from your assistant to a live address, so the people who need it get a link rather than instructions for running it locally.

App Security

Put your identity provider in front of it

Expense claims are personal data. SSO means people sign in with their existing account, and access is scoped so a claimant sees their own claims while reviewers see their team's.

App Security

Connect the system of record with managed credentials

If the checker reads claims from a finance or card system, granting that access to the app rather than pasting a key into it means it can be rotated or revoked without a redeploy.

App Registry

Put a name on it

Every app carries a named owner and an entry IT can see, so the tool does not become nobody's problem when the person who built it changes role.

Cost Management

Know what it costs to run

Per-app spend visibility, with budgets and caps, so an internal tool cannot quietly become a line item nobody can explain.

Questions people ask

What is an expense policy checker?

It evaluates an expense claim against your own policy rules and explains what it found, so reviewers only spend time on the claims that need judgement. It reads the claim for you rather than deciding it for you.

Should the checker auto-reject claims?

Rarely. Auto-approving clearly compliant claims saves real time with little risk. Auto-rejecting creates disputes over edge cases the rules did not anticipate, and the appeal costs more than the review would have. Block, explain, and route to a person.

Where should the policy rules live?

In one editable file, loaded at runtime, in a format the policy owner can change without a code change. Limits vary by city, category, role and season, and a policy encoded across the codebase stops being maintainable almost immediately.

How do I handle a policy change without breaking old claims?

Version the rules with an effective-from date, and record which version each claim was checked against. Otherwise raising a limit silently rewrites the history of claims that were correctly assessed at the time.

What makes a policy checker actually reduce work?

Explaining rather than verdicting, and letting claimants check before they submit. Most breaches are people not knowing the limit, so a pre-submission check removes the claim from the queue before it enters it.

Can I build this without an engineering team?

Yes. The prompts here are written for Claude Code, Codex or Cursor and build it in stages. What no tool decides for you is the severity of each rule, which is the judgement that makes the queue useful or useless.

Last reviewed September 2026.

Build it, then run it properly

Helix is the governed runtime for AI-built apps. Deploy what you build, put SSO in front of it, connect it to your systems with managed credentials, and give it a named owner.