Clio ↔ QuickBooks Online Multi-Currency Sync — Technical Proof

A focused proof built around the accounting and reliability risks in the specification, rather than a re-creation of the whole integration. Every figure below is computed live by the engine on page load; nothing here is a screenshot or a fixture of a result.

Demo / mock providersHome currency USDForeign EURReporting date 2026-07-31All scenarios passReconciliation MISMATCH
7
Synced
3
Duplicates skipped
1
Echoes suppressed
1
Blocked by rule
1
Retryable failures
0
Still pending retry
1
Balance mismatches
13
Audit entries

Foreign exchange

The specification asks for the rate to be captured at transaction date rather than sync date, and for realised and unrealised differences to stay distinct. Those are two separate problems and they are solved separately.

Realised · posts to the ledger

Settled invoice — EUR 10,000.00

Recognised 2026-07-01 @ 1.10USD 11,000.00
Settled 2026-07-20 @ 1.13USD 11,300.00
Realised FX gainFX_GAIN_REALISEDUSD 300.00

EUR 10,000.00 settled on 2026-07-20 at 1.13 is worth USD 11,300.00. The same amount was recognised on 2026-07-01 at 1.10, i.e. USD 11,000.00. The USD 300.00 difference is realised because the cash actually moved.

Unrealised · reported, not posted

Open receivable — EUR 6,000.00

Carried 2026-07-01 @ 1.10USD 6,600.00
Retranslated 2026-07-31 @ 1.07USD 6,420.00
Unrealised FX loss-USD 180.00

EUR 6,000.00 is still outstanding at 2026-07-31. Carried at 1.10 it stands at USD 6,600.00; retranslated at the 2026-07-31 rate of 1.07 it is USD 6,420.00. The -USD 180.00 difference is unrealised: no cash has moved and the rate can still change before settlement.

Double-counting guard. Asking for an unrealised revaluation of an invoice that has already been settled throws rather than returning a plausible number: ALREADY_SETTLED: Invoice INVOICE:CLIO:4101 is fully settled as at 2026-07-31; its FX difference is realised, not unrealised. Revaluing it would double-count.
Demo accounting policy. The unrealised adjustment is reported and not posted. QuickBooks performs its own home-currency adjustment for open foreign balances, so a production deployment must decide who owns that entry — that is the client's accountant's call, not the integration's.

Demo scenarios

Each scenario names the production risk it addresses and checks its own outcome. The same objects rendered here are asserted against in the test suite, so the dashboard cannot drift away from what the tests prove.

A

EUR invoice syncs to QuickBooks at the transaction-date rate

PASS

Syncing a historical invoice with today’s rate silently restates revenue.

  • Clio bill INV-2026-001SYNCEDEUR 10,000 issued 2026-07-01.
  • Rate capturedAS OF TXN DATERate 1.10 as of 2026-07-01; home value USD 11000.00.
  • QBO invoicecreated 1001CurrencyRef EUR, ExchangeRate 1.10.

EUR 10,000 at 1.10 as of 2026-07-01 = USD 11000.00. The sync ran on 2026-09-14 and that date appears nowhere in the valuation.

B

A redelivered webhook does not create a second invoice

PASS

Webhook redelivery is normal. Without idempotency it doubles revenue.

  • First deliverySYNCEDCreated QBO invoice 1001.
  • Redelivery (same record, same etag)SKIPPED_DUPLICATEIdempotency key already applied; no provider call made.
  • QBO invoices now1Count did not increase.

The duplicate is recognised by provider + entity + id + version + payload hash, before any write.

C

Settlement at a different rate produces a realised FX gain

PASS

Booking the payment at the invoice rate hides a real cash difference.

  • Clio receipt 8801SYNCEDEUR 10,000 received 2026-07-20 at 1.13.
  • Home value at recognitionUSD 11,000.00Invoice date rate 1.10.
  • Home value at settlementUSD 11,300.00Payment date rate 1.13.
  • Realised FXGAIN USD 300.00Posted to FX_GAIN_REALISED.

EUR 10,000.00 settled on 2026-07-20 at 1.13 is worth USD 11,300.00. The same amount was recognised on 2026-07-01 at 1.10, i.e. USD 11,000.00. The USD 300.00 difference is realised because the cash actually moved.

E

A trust-to-operating transfer is blocked, not best-effort

PASS

Client money posted to an operating account is a reportable breach, not a data-quality issue.

  • Clio transfer 8802BLOCKED_ACCOUNTING_RULEEUR 2,500 out of Client Trust Bank into Operating Bank.
  • Fund typeTRUSTTaken from the SOURCE account: it is still client money while it moves.
  • Rule triggeredTRUST_INTO_OPERATING_ACCOUNTClient/trust money cannot be posted to the operating account Operating Bank - EUR. Trust funds belong to the client until they are properly earned and transferred.
  • Remediationguidance recordedPost the receipt to a trust bank account. If this is an earned-fee transfer, model it as an explicit two-sided trust-to-operating transfer with its own approval, not as a direct posting.

Guardrails run before the provider call, so a blocked posting costs zero mutations. These are configurable guardrails, not a claim of IOLTA/SRA compliance.

F

A provider failure parks one record and replays cleanly

PASS

A failed batch that aborts, or silently drops a record, leaves the ledgers unreconcilable.

  • Bill INV-2026-003RETRYABLE_ERRORQBO returned 503 Service Unavailable
  • Bill INV-2026-004 (same batch)SYNCEDUnrelated record was not affected by the failure.
  • Retry queue1 pendingBackoff 60s, attempt 1 of 5.
  • After backoff elapsesSYNCEDCreated QBO invoice 1004.
  • Replay once moreSKIPPED_DUPLICATESuccessful replay is still idempotent.

No idempotency key is recorded on failure, so the replay is allowed to do the work; one is recorded on success, so any further replay is a no-op.

D

An open receivable carries unrealised FX, kept separate from realised

PASS

Conflating the two double-counts the same currency movement, or posts a gain that has not happened.

  • Clio bill INV-2026-002SYNCEDEUR 6,000 still open at 2026-07-31.
  • Carried at 1.10USD 6,600.00Transaction-date measurement.
  • Retranslated at 1.07USD 6,420.00Reporting-date measurement.
  • Unrealised FXLOSS -USD 180.00postsToLedger = false. Reported only; QBO performs its own home-currency adjustment.
  • Double-count guardALREADY_SETTLEDALREADY_SETTLED: Invoice INVOICE:CLIO:4101 is fully settled as at 2026-07-31; its FX difference is realised, not unrealised. Revaluing it would double-count.

EUR 6,000.00 is still outstanding at 2026-07-31. Carried at 1.10 it stands at USD 6,600.00; retranslated at the 2026-07-31 rate of 1.07 it is USD 6,420.00. The -USD 180.00 difference is unrealised: no cash has moved and the rate can still change before settlement.

G

Reconciliation surfaces a difference the sync never saw

PASS

Every event reporting SYNCED does not mean the two ledgers agree.

  • Operating Bank - EURMATCHClio 10000.00 vs QBO 10000.00, difference 0.00.
  • Operating Bank - USDMATCHClio 25000.00 vs QBO 25000.00, difference 0.00.
  • Client Trust Bank - EURMISMATCHClio 8000.00 vs QBO 7900.00, difference 100.00.

Balances are derived independently from each system, not from sync outcomes, which is the only way a manual edit or an out-of-band entry is ever caught.

H

A mirrored update does not bounce back and forth forever

PASS

Bi-directional sync plus webhooks is a loop generator: each write triggers the other side.

  • QBO invoice 1001 to ClioSYNCEDCreated Clio bill 9001; link records the content fingerprint.
  • Clio webhook for that billSKIPPED_ECHONew etag, identical economics: recognised as our own write returning.
  • Clio bills created1The echo produced no second record.
  • QBO payment 2001SYNCEDMirrored to Clio payment 9002.
  • Same QBO payment againSKIPPED_DUPLICATEDuplicate suppressed on the QBO to Clio direction too.

The fingerprint covers economic content only. Etag and SyncToken change on every mirrored write, so a version-based check would loop forever; a content-based one terminates on the first echo.

Accounting guardrail — blocked posting

Guardrails are evaluated before any provider call, so a blocked posting costs zero mutations in either system.

BLOCKED_ACCOUNTING_RULE — trust funds into an operating account

Clio transfer 8802
BLOCKED_ACCOUNTING_RULE EUR 2,500 out of Client Trust Bank into Operating Bank.
Fund type
TRUST Taken from the SOURCE account: it is still client money while it moves.
Rule triggered
TRUST_INTO_OPERATING_ACCOUNT Client/trust money cannot be posted to the operating account Operating Bank - EUR. Trust funds belong to the client until they are properly earned and transferred.
Remediation
guidance recorded Post the receipt to a trust bank account. If this is an earned-fee transfer, model it as an explicit two-sided trust-to-operating transfer with its own approval, not as a direct posting.
Scope. These are configurable accounting guardrails, not a claim of IOLTA, SRA or any other jurisdiction's trust-accounting compliance. The value is the enforcement point: production rules specialise this layer by jurisdiction and by firm policy.

Transactions

Note the rate as-of column: it always equals the transaction date, never the date this page was rendered.

EntityDirectionSource → targetTxn dateForeignRateRate as ofHomeFundAccountStatus
INVOICECLIO to QBOCLIO:4101QBO:10012026-07-01EUR 10000.001.102026-07-01USD 11000.00OPERATINGAccounts Receivable (A/R)SYNCED
PAYMENTCLIO to QBOCLIO:8801QBO:10022026-07-20EUR 10000.001.132026-07-20USD 11300.00OPERATINGOperating Bank - EURSYNCED
PAYMENTCLIO to QBOCLIO:8802-2026-07-20EUR 2500.001.132026-07-20USD 2825.00TRUSTOperating Bank - EURBLOCKED_ACCOUNTING_RULE
INVOICECLIO to QBOCLIO:4103-2026-07-15EUR 4200.001.092026-07-15USD 4578.00OPERATINGAccounts Receivable (A/R)RETRYABLE_ERROR
INVOICECLIO to QBOCLIO:4104QBO:10032026-07-15EUR 1800.001.092026-07-15USD 1962.00OPERATINGAccounts Receivable (A/R)SYNCED
INVOICECLIO to QBOCLIO:4103QBO:10042026-07-15EUR 4200.001.092026-07-15USD 4578.00OPERATINGAccounts Receivable (A/R)SYNCED
INVOICECLIO to QBOCLIO:4102QBO:10052026-07-01EUR 6000.001.102026-07-01USD 6600.00OPERATINGAccounts Receivable (A/R)SYNCED
INVOICEQBO to CLIOQBO:1001CLIO:90012026-07-01EUR 10000.001.102026-07-01USD 11000.00OPERATINGAccounts Receivable (A/R)SYNCED
INVOICECLIO to QBOCLIO:9001QBO:10012026-07-01EUR 10000.001.102026-07-01USD 11000.00OPERATINGAccounts Receivable (A/R)SKIPPED_ECHO
PAYMENTQBO to CLIOQBO:2001CLIO:90022026-07-20EUR 10000.001.132026-07-20USD 11300.00OPERATINGOperating Bank - EURSYNCED

Reconciliation

Balances are derived independently from each system, not from sync outcomes. 1 of 3 balances disagree as at 2026-07-31 (largest: Client Trust Bank - EUR, EUR 100.00).

AccountFundCurrencyClioQuickBooksDifferenceStatus
Operating Bank - EUROPERATINGEUR10000.0010000.000.00MATCH
Operating Bank - USDOPERATINGUSD25000.0025000.000.00MATCH
Client Trust Bank - EURTRUSTEUR8000.007900.00100.00MISMATCH

Replayable error queue

A failed record parks here with its error and next attempt time while its batch siblings carry on. Replaying goes back through the same idempotent pipeline.

EventEntitySourceAttemptStatusNext retry atLast error
evt-F-1INVOICECLIO:41031SUCCEEDED2026-09-14 02:08:38

Audit trail — Clio bill 4101

Every attempted mutation is recorded, including the ones that were skipped, blocked or failed. This is the “why does this QuickBooks transaction exist?” query. Payloads are stored as hashes; no secrets or raw provider bodies are retained.

Audit idDirectionSourceTargetForeignRateHomeActionResultPayload hash
audit-1CLIO->QBOCLIO:4101QBO:1001EUR 10000.001.10USD 11000.00CREATESYNCED9d4acd216900
audit-2CLIO->QBOCLIO:4101QBO:1001 SKIPSKIPPED_DUPLICATE9d4acd216900
Webhook security boundary. Clio signs deliveries with HMAC-SHA256 over the raw body in X-Hook-Signature, compared in constant time. Correctly signed: ACCEPTED - HMAC-SHA256 (hex) matched. Signed with the wrong secret: REJECTED - Signature mismatch - payload rejected without processing. Clio subscriptions also expire (3 days by default, 31 maximum), so renewal is a scheduled obligation.

Entity mapping

FromToNote
Clio ContactQBO CustomerLinked before financial entities so identities resolve.
Clio MatterQBO Class / sub-customerQBO has no native matter concept.
Clio BillQBO InvoiceCurrencyRef + ExchangeRate written from the stored transaction-date rate.
Clio PaymentQBO PaymentLinkedTxn points at the mapped QBO invoice id.
QBO InvoiceClio BillReverse direction; echo suppressed by content fingerprint.
QBO PaymentClio PaymentDeposit account determines fund type.

Chart-of-accounts mapping

Demo labels and account ids only. Production values come from the firm's actual QuickBooks chart of accounts and their accountant's posting specification — the mapping is data, not code.

Clio conceptQuickBooks accountFund scope / currency
bank_account:operating_usdOperating Bank - USD [35]OPERATING / USD
bank_account:operating_eurOperating Bank - EUR [36]OPERATING / EUR
bank_account:trust_usdClient Trust Bank - USD [41]TRUST / USD
bank_account:trust_eurClient Trust Bank - EUR [42]TRUST / EUR
billAccounts Receivable (A/R) [84]OPERATING / any currency
activity:TimeEntryLegal Fee Income [79]NONE / any currency
activity:ExpenseEntryRecoverable Client Costs [80]NONE / any currency
(QBO only)Realised FX Gain [91]NONE / USD
(QBO only)Realised FX Loss [92]NONE / USD
(QBO only)Unrealised FX Revaluation (reporting only) [93]NONE / USD / reporting only