Start here
CareRite has thirty-five American Express accounts, one per facility, and many people spending on each. The statement arrives once a month and names none of them.
Measured on CareRite's own ledger, 402,425 card lines have no document behind them. Of 76,879 of those lines, all of them share twenty posting dates — because the ledger records when the batch was keyed, not when the card was swiped. There is no cardholder on the line, no merchant we can rely on, and no receipt.
So by the time anybody looks, four facts are missing and three of them are unrecoverable:
This system captures all four at the moment of the purchase, from the person who made it, while they are still standing in the shop. Then it matches what was captured against the statement when it arrives, and tells somebody exactly which charges still have nothing behind them and who to ask.
Four things happen, in this order, every month.
If you carry a card
Three minutes to set up, about eight seconds per receipt after that.
Under My cards, type the last four digits from the front of the plastic and pick your facility. That is all we want.
Your claim works immediately. An administrator confirms it afterwards; you do not wait for that.
Open the camera and point it at the receipt on a flat surface. It takes the picture itself. You do not press anything.
It is watching three things, and all three show on screen as you move:
Sharpness
Whether the image is in focus. If it will not rise, you are too close — most phones cannot focus nearer than about 10 cm.
Steady
Whether your hands are still. Rest an elbow on something. This is the one that fails most.
Fills frame
Whether the receipt is actually in the picture. Fill the four corners; it is measuring paper, not the table.
When all three hold for about half a second, the corners turn green, a ring fills, and it fires. The phone buzzes. If one is failing, the words at the bottom tell you which — "move closer", "hold still" — so move the phone rather than jabbing the shutter.
There is a manual shutter and an Auto switch if you would rather do it yourself, and a "choose a photo I already took" button that works on any phone, with no camera permission at all.
The paper is read automatically, so the amount and the shop name usually fill themselves in. Check them — the reader is good, not perfect.
Then write what it was for, in your own words: "soda and paper plates for paint night". The account is suggested from those words and the suggestion says why it chose. Tap it if it is right, pick another if it is not, or leave it — AP will code it.
Press Send it to AP. Done.
If a charge arrives on your card with no receipt behind it, it appears under Asked of me and you get an email. If you still have the paper, photograph it. If you never got one — a vending machine, a parking meter — photograph whatever you have and say so in the note. A note saying "no receipt was given, this was parking at the hospital" is a real answer and AP can work with it.
If you run a facility
Re-uploading the same file is harmless. People already on the system are updated, not duplicated, and anybody already signed in keeps their password — a re-upload never invalidates a working logon.
If the AMEX export prints a name against a plastic — a "Card Member" column — put it on the card under Cards. It is the only field that ties a statement line to a person without a human looking, and every month you have it is a month of matching that happens by itself.
The overview shows these first and in red, because a charge arriving on a card with no holder cannot be chased at all — there is nobody to ask. Assigning somebody takes two clicks and it is the single highest-value thing on the screen.
If you are in accounts payable
Export the month from the AMEX site as a CSV and drop it on Statement. The column names differ depending on where in their site you exported from, so it maps them by name and shows you what it read as what before anything is saved, with the first twenty lines as it understood them.
Re-importing the same month is harmless — a charge is recognised by card, date, amount and reference, so duplicates are counted and skipped.
On Reconcile, press "Look for matches". It proposes, and confirms by itself only where there was never a choice to make: an exact amount, on the same card, inside the window, with no other receipt that could have been it. One ambiguity anywhere and a person decides.
Every proposal carries the reason in words — "the amount is exact; the same card ••4417; 2 days before posting". Read it. That sentence is the difference between confirming a match and clicking a button.
On Receipts, anything still without an account has a dropdown right on the row. A correction made here is marked as an AP correction, so the difference between what the cardholder chose and what was right stays visible.
Watch the worth a look line: it appears when what the person typed and what the paper says disagree — a transposed amount, a different card on the slip. That is the line that catches real errors.
Chase is every charge with nothing behind it, oldest and largest first, with who to ask already worked out. Select, press ask. CareRite sends the emails from the real mail domain on its next run.
If you run the box
This box is public. CareRite is not. Nothing about that is accidental and it is the constraint the whole design is built around.
CareRite → the box
Always. CareRite pushes the facility list, the chart of accounts and the card list up; pulls receipts and their images down; takes the chase queue and sends it; reports back what reached DocStar.
The box → CareRite
Never. There is no CareRite hostname in this process, no credential for it, and no outbound queue aimed at it. If this box is taken tomorrow the attacker inherits receipt images, a facility list and a chart of accounts — and no door.
The one outbound call the box makes is to Azure, for reading receipts, under CareRite's existing Microsoft agreement. Set OCR_MODE=deferred and even that stops: CareRite reads the images on the inside and pushes the results back up. The box then dials nothing at all.
Two, narrow, not one with everything:
The puller cannot push reference data and the pusher cannot read an image, so a leaked token is half a problem instead of a whole one. Every call either makes is logged on the Plumbing screen with its name, address and row count.
In this box's own Postgres, as bytes — so the whole machine moves to another in one dump. CareRite pulls each image into Azure blob, which is its permanent home under our own key, and acknowledges with the sha256. Only then may the local copy be dropped, after PRUNE_AFTER_DAYS. A row is never pruned on the pull alone — a network error would otherwise lose the only good copy.
One file: the database, the config and the unit files. Point the Cloudflare tunnel at the new host and it is done. Nothing is stored outside Postgres, which is the whole reason the images are in it.
The machine this runs on already carries other services, has ufw set to drop on INPUT and FORWARD, and runs coturn on 49152–65535. Installing Docker on it inserts rules ahead of ufw and would quietly undo that firewall. Two systemd units and the host's own Postgres instead.
When it goes wrong
| What you see | What it is | What to do |
|---|---|---|
| The code is refused twice in a row | The phone's clock has drifted. The codes are time-based, so a phone two minutes out produces codes that were correct two minutes ago. | Turn on automatic date and time on the phone. It fixes itself within a minute. |
| "The camera was refused" | The browser was told no once and remembers. | Allow the camera in the site settings, or just use "choose a photo I already took" — it needs no permission and files identically. |
| Sharpness never rises | Too close. Most phones cannot focus nearer than about 10 cm. | Move back until the whole receipt fits, then hold still. |
| "That image has been filed into the document store" | The image was pulled to Azure and the local copy pruned. Nothing is lost. | Look in DocStar. Pruning is the retention policy working. |
| A charge sits on the chase list with "nobody on this card" | No cardholder is assigned, so there is literally no one to email. | Cards → add a holder. Then the chase works. |
| "That is the same photograph you sent" | Identical bytes arrived twice. Both are kept, flagged. | Nothing, unless they really are two purchases — then take the second one again rather than resending the first. |
| Plumbing says "Last pull: never" | CareRite's puller is not running, or its token is wrong. Receipts are accumulating safely here meanwhile. | Check the timer on the CareRite side; the box cannot and must not do anything about it. |
| OCR says "unknown" | The reader could not be reached. Note that this is NOT "unreadable" — "I could not tell" and "there is nothing there" are different answers. | Nothing. The worker retries. A genuine unreadable says so. |
An administrator presses "Lost their phone" on the People screen. That clears the password and the authenticator, signs the person out everywhere, and issues a fresh setup link. Clearing only the second factor would leave a one-factor account, so it does not offer that.
Their receipts and history are untouched by all of it.
CareRite Receipts. Questions about a receipt go to your facility's AP contact; questions about the system go to whoever set you up.