Skip to main content

Trust surface

What we promise, and how to verify.

Every promise here links to the code, the CI gate, the runbook, or the legal document that backs it. You should not have to take our word for any of them.

15

Promises

28

Files you can read

0

Language models

24h

Default retention

Fifteen promises

Fifteen things we do. Fifteen ways to check we do them.

For the cook

Your invoice is read once. The original is locked. No model reads what you sent.

For the bookkeeper

Templates plus rules. Per-row confidence. Signed CSV export. Hash-chained audit log. No data leaves our infrastructure for any model.

We will never

  • ·Train any model on your invoice content.
  • ·Send your invoices to a human reviewer offshore.
  • ·Add a chat widget that watches you on the page.
  • ·Sell or share aggregated data about your spend.
  • ·Lock you out of your export.

What we collect, what we never do

We collect

  • Email (when you sign up)
  • Invoice content from PDFs you upload
  • Vendor names and totals
  • Line items and dates
  • Per-row confidence scores

We never collect

  • Names of your staff
  • Names of your customers
  • Where any of you live
  • Your phone unless you ask us to call
  • Your bank account or your card on file

Vendor names are vendor names. We do not collect anything else about you, your staff, your customers, or where any of you live.

  1. 01
    / 15

    No language model ever reads your invoice content.

    In plain English

    We read your invoice with open-source software that finds the page layout (docling), plus templates we wrote for each vendor — there is no language model in the read path. A check runs on every code change and stops the release if anyone tries to add a language-model library.

    Your invoice goes through page-layout extraction and vendor templates into the structured ledger. No language model is in the chain.INVOICEyour scanDOCLINGpage layoutTEMPLATEvendor rulesLEDGERsearchableNo language model anywhere in this chain.

    We read the page layout with a small open-source library called docling, plus our own per-vendor templates. Our build fails any commit that pulls in a language-model SDK.

  2. 02
    / 15

    We never train any model on your invoice content. We do not have a model to train.

    In plain English

    We have no model to train. The reading process is built from rules and per-vendor templates, not from software that learns from inputs. Even if we wanted to use your data, the system would not know how.

    We use rules, per-vendor templates, and docling structure extraction. The contracts and the code agree.

  3. 03
    / 15

    Raw invoice files you have not locked have a default retention of 24 hours after extraction; you can delete any document yourself at any time, and a file you have locked is kept encrypted to you until you delete it.

    In plain English

    An unlocked scan is kept for 24 hours after we read it, then we delete it. A locked scan is kept until you choose to delete it. You can also delete any document on your own at any time, and the file is deleted within 60 seconds.

    An unlocked scan is kept for 24 hours and then deleted. A locked scan stays until you choose to delete it.UNLOCKEDkept 24 hours, then deleted0h24h · deletedLOCKEDstays until you delete it

    An explicit delete or erasure request removes the file within 60 seconds today. The time-based reaper that enforces the 24-hour default is being switched on (gated off until verified against the live database); until then, automatic purge is not guaranteed and explicit deletion is the reliable path. The reaper only ever deletes UNLOCKED (plaintext) files — a locked file is the retained copy and a safety-net refuses to delete a file whose lock half-completed, so a locked document is never silently lost. Cloudflare bucket policy aborts incomplete uploads after 1 hour.

  4. 04
    / 15

    You can lock your scanned originals so only your recovery phrase opens them — we cannot. Your searchable ledger keeps working.

    In plain English

    You can turn on a lock for your scanned originals. Once it is on, only a phrase that lives on your device can reopen them — not us, not anyone else. Your ledger (vendor names, totals, line items) stays readable to us so the app keeps working on every device. If you ever lose the phrase, the locked scans cannot be reopened, but the ledger is still yours.

    The scanned original is locked to your recovery phrase. The structured ledger stays readable so the app keeps working.YOUR SCANLOCKED ORIGINALonly your phrase opens itLEDGERsearchable, syncs everywhere

    Turning on the lock makes a 12-word recovery phrase on your device. From it your device derives a key (Argon2id) that, through an elliptic-curve key envelope, protects a per-document key. The server only ever holds your PUBLIC key: after reading a document once to extract your ledger, it encrypts the stored original to that public key and discards the plaintext. Only your phrase reopens a stored original; the server has no way to. Your vendor names, totals and line items stay readable to the server by design — that carve-out is what makes search and cross-device sync work, and it is deliberate, not a gap. Lose the phrase and your stored originals cannot be reopened; your ledger stays. This is opt-in and enforced in code and tests; it is not yet independently verified in production.

  5. 05
    / 15

    Every action in your account is hash-chained. You can verify the chain at any time.

    In plain English

    Every action on your account is recorded in a way that cannot be quietly rewritten later. Each entry includes a fingerprint of the one before, so a reviewer can walk the chain and prove nothing was altered after the fact.

    Every action in your account is hash-chained. Each entry includes a fingerprint of the previous one; walking the chain proves nothing was rewritten.READInvoice #432PREVHASHa4f1…2b08CONFIRMVendor: Del SolPREVa4f1…HASH9c30…d7e2POSTQuickBooksPREV9c30…HASHe811…4a56Each entry includes a fingerprint of the one before it.

    Each audit event records prev_hash + event_hash. The verify endpoint walks the chain and returns valid:true or names the first break.

  6. 06
    / 15

    Same invoice, same numbers, every time. Two runs match to the byte.

    In plain English

    The same invoice produces the same numbers every time we read it. The read uses a fixed clock and a fixed counter, and a test in our build fails if the output ever drifts by a single byte between runs.

    Our reader runs against a frozen clock and a seeded ID generator in the test suite. A byte-equal gate fails any commit that lets the output drift between runs.

  7. 07
    / 15

    One tenant cannot read another tenant's records, even with the database password.

    In plain English

    Your data is yours alone. Even if someone got the database password, the database itself enforces a filter that hides every workspace from every other workspace. A test in our build sets up two fake workspaces and proves one cannot see the other.

    Two workspaces, one database. The filter that hides each workspace from the others lives in the database itself, one layer below the application.YOUR WORKSPACEANOTHER WORKSPACEDATABASEThe database enforces the filter, not the application.

    Postgres row-level security with FORCE + WITH CHECK on every table holding customer data. The cross-tenant pgTAP test asserts the contract.

  8. 08
    / 15

    Signing out really signs out. A revoked session cannot mint new access tokens.

    In plain English

    Sign-out ends your session on our side right away. Any token that was tied to it stops working as soon as the sign-out call returns, well before its scheduled expiration.

    Sessions live in a D1 table; the access JWT lasts 15 minutes; refresh consults sessions.revoked_at on every call.

  9. 09
    / 15

    Our sub-processor list is the public source of truth. We send 30 days notice before any change.

    In plain English

    We publish a list of every third party that helps us run the product. The list stays current — a check fails the build if the docs go stale. We give you 30 days notice before any change.

    A CI gate scans IaC + route handlers for sub-processor hostnames; the freshness check fails the build if the docs are stale.

  10. 10
    / 15

    You can export every record we hold for your workspace. The export is signed.

    In plain English

    You can download everything we hold for your workspace. The CSV button is free and lives in the product. The fuller export is signed so a reviewer can confirm it has not been tampered with.

    CSV is live and self-serve from the product. The full JSON portability bundle (with audit log) is signed against a published receipt key and rate-limited to ten exports per day per org.

  11. 11
    / 15

    We do not include customer content in application logs or error reports.

    In plain English

    Your invoice content does not go into our application logs or error reports. A scrubber runs before anything gets written to a log, and a test fails the build if any payload sneaks through.

    A PII scrubber runs Sentry beforeSend + sanitises every audit-log value before D1 write. The fixture test fails any payload that leaks through.

  12. 12
    / 15

    If you send a photo, we straighten and clean a copy in memory to read it. The cleaned copy is not kept.

    In plain English

    If you send a photo of an invoice, we straighten and clean the picture in our memory so we can read it well — then we let that cleaned copy go. The original photo you sent stays subject to the same lock and 24-hour rules as a PDF.

    A phone photo is straightened and cleaned in memory to read it, then the cleaned working copy is released. The original photo stays subject to the same rules as a PDF.ORIGINALtilted, low lightWORKING COPYin memory onlyrotate · denoise · lightRELEASEDnever touches diskThe cleaned working copy never touches disk.

    Phone photos are uneven: tilted, dim, glared. Inside our extraction service we rotate, dewarp, denoise and brighten a copy in memory using opencv-python-headless, read it, then let that copy go. The file you uploaded is the only persisted original (subject to the lock and 24-hour retention promises above). The cleaned working copy never touches disk; a CI gate fails any commit that adds a print() inside the service so the copy cannot accidentally land in a log.

  13. 13
    / 15

    The /demo page never asks for your invoice.

    In plain English

    The /demo page does not have an upload form. It walks a guided tour over sample data we wrote ourselves, so you can see how the product works without sending us anything. The page has no path to call any upload-style endpoint.

    There is no upload surface on /demo. The page renders a guided tour over hand-authored sample data so a visitor can see how we read, catch, and file an invoice without a single byte moving. The /v1/demo/extract API endpoint we keep for internal tests remains in-memory-only and persistence-gated; the CI script that polices the route runs on every build.

  14. 14
    / 15

    When you save your demo row, we hold it for 60 minutes — no longer — and only attach it after you confirm.

    In plain English

    If you walk through the demo and want to keep the row in your account, we hold it for one hour while you sign in. After you sign in, we ask you once whether to add it or drop it. Either choice deletes our copy. If you never sign in, the row expires on its own.

    POST /v1/onboarding/demo-handoff stashes the parsed envelope under a one-time token in KV with a 60-minute TTL, then emails you a sign-in link. After you verify, /inbox renders a card with two equally-weighted buttons: Add it / Discard. Either action deletes the KV entry. The handoff is anonymous until the moment you click Add it; if you never confirm, it expires.

  15. 15
    / 15

    We do not track you on our marketing site. Our analytics runs on our own server, sets no cookies, and counts visits without identifying you.

    In plain English

    On our public marketing pages we measure how many people visit, nothing about who you are. The counter runs on a server we host ourselves, sets no cookies, and keeps no record of your IP address or browser. It never runs once you are signed in to the product.

    Marketing pages load a first-party counter (Plausible Community Edition that we host ourselves on the same Fly server as our self-hosted error tracking, so the Plausible company is not a sub-processor). The script reads no cookies and stores no per-visitor identifier; it sends each visit to a same-origin endpoint on our own domain. To count a unique visit for the day without keeping who you are, our server takes your browser and IP only in passing, folds them into a one-way hash with a salt that is thrown away every 24 hours, and keeps only the aggregate count — your IP and browser are never written down. The counter is wired into the marketing layout alone, so it cannot load on any signed-in page. We declare Global Privacy Control support and honour it.

Report a privacy flaw

If you find a gap between what we promise and what we do, write to security@muntin.digital. We answer within five business days.

security@muntin.digital · Our PGP key

Bug bounty

We pay $200 to $2,000 for verified privacy or security flaws. We wait 90 days from your report before we write about it in public.

In scope

  • ·Any path that exposes invoice content to a third party.
  • ·Any path that lets one tenant read another's data.
  • ·Any auth break on the magic-link or session cookie.

Out of scope

  • ·Self-XSS that requires the victim to paste code.
  • ·Rate-limit skips with no data leak.
  • ·Reports from automated scanners with no proof.

Subject line: [BUG-BOUNTY] short description

A promise not enforced in code is not a promise. If a link breaks or a claim seems wrong, write to hello@muntin.digital. We owe you an answer.

Words we use on this page in plain operator language: /glossary.