parsr.

For accounting firms with 50–500 SMB clients

AI bookkeeping at production scale.

The firms automating bookkeeping for 100+ SMB clients have one shared problem: every client uploads a different mix of bank statements, invoices, and receipts, in a different bank's format, and the firm is expected to file them in QuickBooks Online for Accountants or Xero Practice Manager without errors. parsr is one API, three doc types, with multi-tenant isolation and an audit trail your senior accountants can sign.

Why firms pick parsr

Built for the firm, not the bookkeeper-of-one

01 / One API

Bank statements, invoices, receipts — same shape

Three endpoints, one auth model, one schema family. Whether the file your client uploaded is a Belfius PDF, a German VAT invoice, or a wrinkled photo of a coffee receipt, the response carries the same fields, confidences, and audit metadata.

02 / Reconciliation

Balance-chain validation reduces month-end errors

Bank-statement responses include validation.balance_chain — a structured verdict that re-runs the running balance and flags inserted or deleted rows. The reconciliation work that ate Friday afternoons becomes a confidence threshold.

03 / Multi-tenant

One org_id per client, isolated end to end

Every parse carries the client's org_id. Audit records, retention policies, billing meters, and rate limits are scoped per-client. Disengaging a client is a single API call — no shared bucket, no leaked context, no awkward reset.

The firm workflow

Five steps from client portal to ledger

Designed around the way modern firms already work — a client portal, a dispatcher, a bookkeeping system, and a senior reviewer for the edge cases.

  1. 01

    01 / client.upload(documents)

    Client uploads bank statements, invoices, and receipts to your firm's portal — typically batched at month-end, sometimes streamed daily. Your portal stores the raw files, tagged with client_id and document_type.

  2. 02

    02 / firm.dispatch(file, org_id=client_id)

    Your dispatcher service forwards each file to the matching parsr endpoint — /v1/parse/bank-statement, /v1/parse/invoice, or /v1/parse/receipt — passing the client's id as metadata.org_id. The same parsr API key works for every client; isolation is at the metadata layer.

  3. 03

    03 / parsr.parse → schema-validated JSON

    parsr returns a versioned, schema-validated payload per document. Bank statements include the balance-chain verdict; invoices include line items and VAT breakdown; receipts include merchant + line items + FX hint. Every field carries a confidence score.

  4. 04

    04 / bookkeeping.import(json, org_id)

    Your integration layer imports the parsed JSON into the bookkeeping system the client uses — QuickBooks Online for Accountants, Xero Practice Manager, Sage, or your firm's internal system. The org_id stays attached so the journal entry lands in the right ledger.

  5. 05

    05 / reviewer.escalate(low_confidence_or_chain_failed)

    Anything below your confidence threshold (we recommend 0.85 for posting, 0.95 for VAT-bearing fields) or any failed balance-chain verdict is escalated to a human reviewer with the bounding box pre-rendered. Everything above flows straight to the ledger.

The integration

Dispatch by client, parse by document type

One firm, many clients, three document types. Each parse carries the client's org_id so audit records, billing, and retention policies stay isolated per-client without your code thinking about it.

firm/dispatcher.pypython
import os
import asyncio
import httpx

API = "https://eu-api.tryparsr.dev"
KEY = os.environ["PARSR_KEY"]  # sk_eu_live_…

ENDPOINT = {
    "bank_statement": f"{API}/v1/parse/bank-statement",
    "invoice":        f"{API}/v1/parse/invoice",
    "receipt":        f"{API}/v1/parse/receipt",
}

POST_THRESHOLD = 0.85   # post straight to the ledger above this
VAT_THRESHOLD  = 0.95   # VAT-bearing fields get a stricter bar

async def dispatch(client_id: str, doc_type: str, file_path: str) -> dict:
    """Parse a single client document. client_id is your firm's internal id;
    we pass it as parsr's org_id so audit, billing, and retention scope
    stay isolated per-client."""
    url = ENDPOINT[doc_type]

    async with httpx.AsyncClient(timeout=60) as http:
        with open(file_path, "rb") as f:
            resp = await http.post(
                f"{url}?wait=60",
                headers={
                    "Authorization": f"Bearer {KEY}",
                    "Idempotency-Key": f"{client_id}:{file_path}",
                },
                files={"file": f},
                data={"metadata": f'{{"org_id":"{client_id}"}}'},
            )
        resp.raise_for_status()
        doc = resp.json()

    # Bank statements: balance-chain verdict gates auto-post
    if doc_type == "bank_statement":
        chain = doc["validation"]["balance_chain"]
        if not chain["valid"]:
            return {"action": "review", "reason": "balance_chain_failed",
                    "client_id": client_id, "doc": doc}

    # Confidence-gated routing into the bookkeeping ledger
    min_conf = min(f["confidence"] for f in doc["fields"].values())
    has_vat = any("vat" in name for name in doc["fields"])
    bar = VAT_THRESHOLD if has_vat else POST_THRESHOLD

    return {
        "action": "post" if min_conf >= bar else "review",
        "client_id": client_id,
        "schema_version": doc["schema_version"],
        "doc": doc,
    }

async def run_batch(client_id: str, files: list[tuple[str, str]]) -> list[dict]:
    """Process a client's monthly drop in parallel, scoped to their org_id."""
    return await asyncio.gather(*(
        dispatch(client_id, doc_type, path) for doc_type, path in files
    ))

What partners ask before signing

The controls your senior partner wants in writing

  • Multi-tenant data isolation: one org_id per client, no shared scope
  • Accountant-grade audit logs — every parse is signed and exportable as NDJSON
  • DORA-compliant retention windows, configurable per-org down to zero
  • GDPR-compliant with a vendor DPA available before signup, written for accountants
  • EU residency by default — Frankfurt + Paris regions, no US fall-through
  • Per-client billing meters, per-client rate limits, per-client retention
  • Schrems II-safe: no US sub-processor sits on the parse path
  • SOC 2 Type II audit window opens Q3 2026 (ISO 27001 to follow)
  • Region-bound API keys (sk_eu_… vs sk_us_…) — no accidental cross-border traffic
  • 30-day file-hash cache — re-uploads of the same statement don't double-bill

Pricing fit

Where firms land on the ladder

Recommended tier

Scale — €399 / month for 25,000 pages

A firm with 50+ SMB clients, each pushing a few hundred pages a month between bank statements, invoices, and receipts, lands cleanly on the Scale tier. Above 100 clients we open an Enterprise floor (~€2,000 / month) with annual commit, custom retention, dedicated regional pinning, and an SLA that names your senior partner. The Free tier (200 pages) is for evaluation; Starter and Growth are for solo bookkeepers, not firms.

Questions firms ask

FAQ

How does parsr keep one client's data isolated from another?

We isolate at the metadata layer: every parse request carries an org_id (you decide the value — typically your internal client_id). Audit records, retention policies, billing meters, rate limits, and the file-hash cache are all scoped per org_id. Two clients of yours cannot share a cache hit. Disengaging a client is a single API call that purges every record tagged with that org_id within the retention window.

Can I produce an audit trail my senior partner can sign?

Yes. Every parse writes a signed audit record with input hash, schema version, model version, requesting key fingerprint, regional endpoint, and the org_id. Records are exportable as NDJSON and can be replayed against the schema to re-derive the JSON deterministically when the model is unchanged. We don't claim a SOC 2 report yet — the audit window opens Q3 2026 — but the audit log itself is production today.

Do you integrate with QuickBooks Online for Accountants and Xero Practice Manager?

We don't ship a closed-box plugin for either, deliberately. parsr returns the canonical fields those systems require — vendor, GL hint, tax-rate code, line items with quantities and tax rates — and your firm's integration layer pushes them into QBOA or XPM via the partner APIs you already authenticate against. Most firms have an integration layer; parsr makes it useful by giving it clean inputs.

What accuracy SLA can you commit to?

We don't ship a single accuracy number, because it's misleading across document types and bank formats. What we commit to: per-field confidence scores you can act on, a balance-chain verdict on every bank statement, schema-versioned outputs, and a documented escalation path for low-confidence fields. On the structured doc types (bank statements, invoices) confidences cluster above 0.95 in production for supported formats; we publish per-format coverage in /coverage. Enterprise contracts can include a custom SLA.

What's the data retention story for my clients' financial records?

By default we retain the raw upload for 30 days (for cache and re-parse on schema upgrade) and the structured JSON for 90 days. Both windows are configurable per-org down to zero — pass retention_days: 0 and we discard the raw file the moment the parse returns. Enterprise contracts can pin retention to a specific Exoscale or Hetzner region, or extend it for clients in regulated industries with statutory retention obligations.

How does GDPR work when I'm the accountant and my client is the data subject?

Under GDPR you (the firm) are the controller of your client's records; we (parsr) are the processor. Our DPA is written with that arrangement in mind — it names you as controller, lists every sub-processor, locks data residency to the EU regions, and includes the standard contractual clauses you need. We can sign a DPA before you sign up; the security page links to a draft you can route to your DPO.

200 free pages. No credit card. No sales call.

Pilot parsr on three of your messier clients. If the multi-tenant isolation, the audit logs, or the balance-chain validation doesn't pass your senior partner's smell test, walk away — we won't argue.

Start the pilot