Fatouraty
Documents10 min read

The paper behind the number: capturing receipts and filing them against the entry

An accountant does not receive a receipt at the moment they post the entry; they receive fifty of them in a pile and work through them later. So a document lands in an inbox before it belongs to anything — and that detail is what makes filing happen at all.

The Fatouraty team

A ledger entry without its source document is an assertion. With one it is evidence. Every tax authority in the region can ask to see the invoice behind a deduction, and "it's somewhere in email" is the answer that turns an ordinary review into an assessment. The distance between books that are correct and books that are provable is not in the entries — it is in the paper behind them.

A document arrives before it belongs to anything

This is where most attachment features fail. They assume you upload a receipt while standing inside the screen of the record it belongs to, whereas the real work is the exact opposite: fifty pieces of paper arrive in a pile or in a folder on a phone, and get sorted two days or a week later. Because the system demands a destination before it will accept the file, the pile stays outside the system forever.

So in Fatouraty a document lands in an inbox first, and is filed afterwards against the record it evidences. Filing is polymorphic — the same convention journal entries and stock movements already use — because one piece of paper can support more than one record: a supplier invoice and the payment that settled it are the same sheet. Four of the linkable types have a record page carrying the attachments strip directly — bill, invoice, contact and employee — and five more are supported through the API without a page to show them on yet: payment, bill payment, journal entry, fixed asset and tax filing.

Filing has two rules that prevent one quiet error and one serious one. First, the target is confirmed to exist inside your own workspace before the link is written — without that, one workspace could file its document against another tenant's bill, invisible to both readers and a dangling reference besides. Second, unfiling returns the document to the inbox rather than deleting it, and deleting is refused outright while a document is filed against anything: evidence should not disappear from underneath a posted entry, and "unfile it there first" makes that a deliberate two-step rather than a slip.

The same receipt, twice

A receipt photographed twice, or forwarded twice by email, is the single most common thing in any pile. Each file gets a fingerprint computed from its original bytes before encryption, unique within one workspace, so a duplicate is recognised rather than filed a second time. The refusal names the copy already on file, so the accountant goes straight to it instead of searching for whether one exists. The reason is accounting, not tidiness: a receipt counted twice is an expense recorded twice.

The fingerprint is deliberately scoped to one workspace. A firm keeping twenty clients' books will meet the same invoice from the same supplier under two different clients, and there it is two legitimate documents rather than one duplicate. An implementation that looked for duplicates across every client would refuse one of them — the kind of error nobody finds until an audit.

What the model reads, and what it is not allowed to do

The file is sent to the model as a document or as an image, and the reply is constrained by a fixed schema so there is no prose to parse: vendor, document number, date, currency, total, tax, and tax registration number. So far this is any other invoice reader. The three differences that matter to an accountant come next.

The automatic read covers four of the five accepted formats: PDF, JPEG, PNG and WebP. HEIC/HEIF — the iPhone camera's default, and so the likeliest way a photographed receipt arrives — is stored, previewed and filed like any other file, but nothing in this stack decodes it, so its fields are typed by hand. The product says exactly that on the document rather than showing a generic failure that leaves the format unmentioned.

  • Nothing it reads reaches the ledger. Every field is a suggestion on the document record, reviewed by a person before it becomes a bill. A wrong figure a human corrects costs seconds; a wrong figure that reaches an entry on its own costs a restatement.
  • Everything comes back as text, not as a decimal number. An amount returns as the string "1234.50" and goes through the same converter every amount in the product goes through. Money never becomes a floating-point number, not even in a suggestion.
  • The currency printed on the page decides the decimals, not the workspace's currency. A Jordanian firm holding a Saudi receipt must read 100.50 as SAR 100.50 and not as 100.500 dinars — the dinar carries three decimal places.

The read deliberately runs on the cheap model tier: pulling fields off a page is the high-volume, structurally simple task that tier exists for, and a smart-tier call per receipt would make capturing a shoebox of paper cost more than the bookkeeping it saves. Two results are also treated as failures rather than as an empty receipt: a safety decline, and a reply truncated for hitting its length limit. Recording "read successfully, found nothing" for either would be a lie the inbox then displays.

The read is queued, not awaited

Reading a page takes anywhere from three seconds to most of a minute. Putting that on the request's own clock produces one of the strangest failures there is: the function is killed at ten seconds on the default tier, the document has already been stored and linked before the kill, so the accountant sees a failed upload, re-uploads, and is told it is a duplicate. A feature that looks broken while working perfectly.

So on upload the document is marked as being read and the job is queued, and the background worker performs it with no clock over it — which means the automatic read needs that worker running, exactly as importing already does. Two things stop a document sitting on "reading" forever: a sweep re-queues anything pending for more than five minutes — the case where the enqueue itself never landed because the queue was down at the moment of upload — and marks anything pending for more than an hour as failed, because retrying forever is not resilience, it is a bill. And "read it again" still runs immediately, capped at twenty seconds: there a person is waiting for an answer, an answer now beats a status that changes later, and it is the one path that works on a deployment running no worker at all.

Where the bytes live, and what may be uploaded

Documents are stored inside the workspace's own database, encrypted under its own key, rather than in an external file store. The obvious alternative — a cloud bucket — is the right answer for a product that accepts arbitrary media, and the wrong one here, for three reasons: a receipt names a supplier, a date and an amount, the same class of information as the contact record this product encrypts with the tenant's key and never lets leave their storage; the desktop build runs on an embedded database with no network at all, where an external store would not be slower, it would not work; and a file written in the same transaction as its metadata cannot half-commit, whereas "the row exists and the file does not" is a state you have to write reconciliation code for.

The cost is stated plainly: the encryption layer takes and returns text, so the bytes are encoded, sealed, and the sealed envelope encoded again — about 1.78 times the original size on disk. At the 10 MB per-file cap the worst row is around 18 MB, which the database stores out-of-line without complaint. That multiplier is exactly why the cap is 10 MB and not 100: this is a store for receipts and supplier invoices and should stay one.

The accepted types are an allowlist, not a blocklist: PDF, JPEG, PNG, WebP, and HEIC/HEIF. The difference is fundamental, because the browser renders whatever the download route hands back, so anything that could carry script must never be storable however convenient — and SVG is the one people always ask for and the one that must never be on the list. The declared type is also whatever the browser felt like sending, so the actual leading bytes are checked, and a mismatch is refused rather than corrected: it is either a broken client or someone testing what the store will take, and neither should end with a file served back under a type nobody chose.

What the auditor does with missing paperwork

The AI auditor flags material bills with no supporting document, an inbox that has become a backlog, and documents that could not be read. The practice board's per-client count carries the inbox figure, because late paperwork never looks late until somebody asks for it.

"Material" here is a thousand units of the bill's own currency, applied per bill. The threshold began as a flat figure in minor units, which meant SAR 1,000 and JOD 100 at the same time — the Gulf dinars carry three decimals — so a Jordanian firm was chased for paperwork on bills a tenth the size of the Saudi firm's next door. Both checks stay entirely silent for a workspace that has never captured a document: a firm that keeps its paperwork elsewhere should not be nagged about every bill it has ever entered.

What this does not do today

It does not file a document against its record for you: filing is a person's decision, and a suggestion stays a suggestion on the document record until somebody accepts it. The automatic read needs an API key and the background worker running; without them everything else still works — upload, preview, filing and search — and the fields are typed by hand, which is what happens today anyway. The cap is 10 MB per file, and uploads are rate limited because this is the only action that both stores unbounded bytes and spends money at an external API on every call. And five of the nine linkable types do not yet have a page for the attachments strip to appear on.

Frequently asked questions

What file types can I upload?

PDF, JPEG, PNG, WebP and HEIC/HEIF only — an allowlist, not a blocklist. The type is checked against the file's actual leading bytes rather than the browser's declaration, and a mismatch is refused rather than corrected. SVG is deliberately excluded because it can carry script. The cap is 10 MB per file. Four of those are read automatically — PDF, JPEG, PNG and WebP — while HEIC/HEIF files are stored and filed with their fields typed by hand.

Does the AI create the bill for me?

No. What is extracted stays a suggestion on the document record — vendor, number, date, currency, total, tax and tax registration number — and a person reviews it before it becomes a bill. That is a design decision rather than a technical limit: a wrong figure a human corrects costs seconds, and one that reaches the ledger on its own costs far more.

Where are documents stored, and are they encrypted?

Inside the workspace's own database, encrypted under its own key, rather than in an external file store. Encryption raises the size on disk by about 1.78 times, which is the stated reason the cap is 10 MB. Serving is protected by the session, the second factor, and the workspace id pinned into the page.

What happens if I upload the same receipt twice?

The second upload is refused, naming the copy already on file so you can go straight to it. The fingerprint is computed from the original bytes and is unique within one workspace only, so two clients each holding the same invoice from the same supplier do not affect one another.

Can I delete a document?

Not while it is filed against a record. Unfile it first — unfiling returns it to the inbox rather than deleting it — and then delete it. The rule exists so that evidence cannot vanish from underneath a posted entry with one slip.

Does capture work without an AI key?

Yes, except for the automatic read itself. Upload, preview, filing, search and duplicate detection all work as they are, and the document's fields are typed by hand. The automatic read additionally needs the background worker running, because it happens on a queue rather than on the request's clock.

Read next

All articles
Practice10 min read

Running a practice: who owns each client, what is due, and what you are waiting for

Every other screen in the product belongs to one client. The practice board belongs to the firm. The difference is not the layout but the question: "which of my clients has nobody on it?" is a question no single client's books can answer, and it is the question behind every deadline that was ever missed.

Read the article
Invoicing8 min read

How to write an invoice: every field, and why it is there

An invoice is three documents wearing one coat: a request for payment, a tax document, and the source record behind two entries in your ledger. Most advice covers the first and skips the other two — which is why so many invoices look perfectly fine and still come back.

Read the article

Put this to work on your own books

Compliant e-invoicing, a real double-entry ledger, and tax reporting — in Arabic and English.

Start free