SAR workflow: tying suspicious activity to the ledger
Filing a Suspicious Activity Report doesn't move money. It DOES freeze funds. The bookkeeping matters.
By Solomon Ajayi · Free to read, no signup
Your AML monitor flags a transaction pattern, a user has been receiving small inbound transfers from many unrelated parties and quickly forwarding to one external account. Classic mule / smurfing pattern. You file a Suspicious Activity Report (SAR) with the relevant FIU (NFIU in Nigeria, FinCEN in US, etc). The user's funds get FROZEN pending investigation, they can't withdraw, can't transfer, but they're still on your books as a liability you owe them. The freeze itself doesn't move money but it MUST be reflected in your ledger so the compliance team's view of 'frozen funds' matches the operational reality.
A freeze does not move money. The funds are still the user's, you still owe them, and the total on your books does not change. What changes is whether the user can touch the balance. That distinction is hard to represent if a wallet is a single number, because a single number cannot be both owed and untouchable at the same time. The ledger answer is to split the liability into two buckets and reclassify between them.
When the SAR fires, you debit User Wallet (Available) and credit User Wallet (Frozen, SAR), moving the balance from one liability account to another. No asset moves, no revenue is recognised, the books stay balanced. Now the compliance team can report exactly how much is frozen by summing one account, operations can deny withdrawals because the available balance is zero, and the user opening the app still sees their money, just under review. When the investigation clears the user, you reverse the entry: Frozen down, Available up.
What makes the freeze trustworthy is the metadata, not the amount. Each entry carries the actor (the compliance tool), the reason code (sar_freeze, sar_cleared), and the SAR case ID, so a future inquiry can prove who froze whose funds, when, and why. Both entries live in the ledger forever, even after the user is cleared, because the regulator may ask about the freeze years after the fact.
Worked example, step by step
User wallet sitting at ₦200,000, flagged for review
Marker: user has ₦200,000 in their wallet at the moment compliance fires the SAR. Their balance is technically still theirs, but operationally untouchable.
| Account | Debit | Credit |
|---|---|---|
| Bank Account (1200) | ₦200,000.00 | |
| User Wallet (Available) (2000) | ₦200,000.00 |
We bootstrap the state: User Wallet (Available) UP ₦200,000 (credit), Bank Account UP ₦200,000 (debit). Now SAR fires.
Freeze: reclassify Available → Frozen (SAR)
SAR filed. Compliance ops triggers the freeze workflow. The wallet balance moves from the AVAILABLE bucket to the FROZEN-SAR bucket. The user opening the app sees their balance, but every action against it is denied with 'account under review.'
| Account | Debit | Credit |
|---|---|---|
| User Wallet (Available) (2000) | ₦200,000.00 | |
| User Wallet (Frozen, SAR) (2150) | ₦200,000.00 |
User Wallet (Available) DOWN ₦200,000 (debit). User Wallet (Frozen, SAR) UP ₦200,000 (credit). Pure reclassification between two liability accounts, no money moved, no P&L. Audit trail: metadata.actor='compliance-tool', reasonCode='sar_freeze', sourceEventId pointing to the SAR case ID for traceability.
Investigation clears the user: release back to Available
Four weeks later, the SAR investigation concludes, the inbound pattern was a legitimate informal money-pool (esusu / ajo). Compliance reverses the freeze.
| Account | Debit | Credit |
|---|---|---|
| User Wallet (Frozen, SAR) (2150) | ₦200,000.00 | |
| User Wallet (Available) (2000) | ₦200,000.00 |
User Wallet (Frozen, SAR) DOWN ₦200,000 (debit). User Wallet (Available) UP ₦200,000 (credit). The reverse of the freeze entry. The audit trail keeps both entries forever, the user, the regulator, and any downstream auditor can reconstruct the WHY at any future date.
Takeaway
SAR freezes are LEDGER events, not just operational flags. The user's funds stay on your books as a liability, but in a dedicated 'Frozen-SAR' liability bucket so compliance can report 'currently frozen' as a real number, regulators can audit the trail, and ops can confidently deny withdrawal requests with the system-level enforcement matching. Build the freeze workflow as a pair of metadata-rich entries (freeze + release) keyed by the SAR case ID, so any future inquiry can trace WHO froze WHAT funds WHEN and why.
Practice this on a real ledger
Reading is half of it. Open this lesson in the lab to post the entries yourself against a real Postgres-backed double-entry ledger, with the validation on. Free, your sandbox is yours.