WritingCompliance
Ongoing sanctions screening: per event, nightly batch or on list change
Three ways to keep screening customers after onboarding, what each costs in latency, load and missed hits, and the questions that settle the choice.
Screening a customer against sanctions and watchlists at onboarding is the easy part. The customer is in front of you, the check is one call, and a hit stops the application before anything has happened. The hard part starts the day after: the customer's details change, the lists change, and the account keeps moving money. Ongoing monitoring is expected of any regulated business, but the rules do not say when to look again, and that timing is a design decision with real costs on each side.
There are three ways to keep screening after onboarding. You can screen at the moment an event happens (a payout, a new beneficiary, a change of director), you can rescreen the whole customer base on a schedule (typically nightly), or you can rescreen only when a list changes, and only the customers a change could affect. Most teams start with the nightly batch because it is the simplest to build, and most teams later wish they had started somewhere else. When I worked on a centralised KYB, KYC and AML dashboard at Cellbunq, consolidating thousands of data sources behind one view, the lesson that stuck was that the lookup itself is cheap; what costs is deciding when to run it and what a result is allowed to change.
What decides it
| Criterion | Per event | Nightly batch | On list change |
|---|---|---|---|
| Time to catch a new designation | Next event only | Up to 24 hours | Minutes to hours |
| Can it stop an action before it completes | Yes | No | No, unless paired |
| Covers dormant customers | No | Yes | Yes |
| Provider calls per day | One per event | One per customer | Only affected records |
| Extra latency on the customer path | Yes | None | None |
| Build and operate | Low to medium | Low | Medium to high |
| Reviewer noise | Spread across the day | One morning spike | Small, relevant batches |
Two of these rows matter more than the rest. The first is whether a hit needs to stop an action before it completes. If the answer is yes for any action (and for a payout it almost always is), nothing that runs after the fact is enough on its own. The second is dormant customers: an account that has not transacted for a year never triggers an event, so per-event screening alone never looks at it, and a designation added last month sits unnoticed.
Per event
Best for Any action that must not complete against a listed party
Screen the relevant parties (customer, beneficiary, counterparty) synchronously, as part of the action itself, and let the result gate it.
- Strengths: the check and the decision happen in the same transaction, so nothing goes out to a party who was listed at the moment you looked. Cost scales with activity, not with the size of the base, and an idle base costs nothing. The result is easy to explain later, because the screening record sits next to the action it gated.
- Costs: it adds a dependency and its latency to a path customers are waiting on, so the provider's timeout and error behaviour become your problem. A provider outage means choosing between blocking every payout and letting them through unscreened; that choice needs to be made in advance, in code, not during the incident. It never revisits customers who do nothing, and a fuzzy name match on every event means the same false positive reappears every time that customer pays.
Nightly batch
Best for A small base with no time-critical actions, or as a safety net
Once a day, rescreen every active customer and their related parties against the current lists, and queue new hits for review.
- Strengths: it is the simplest to build and reason about: one job, one run, one report. It covers every customer, dormant or not, and it puts no latency on anything the customer sees. Reviewers get a predictable workload each morning.
- Costs: a designation added at 09:00 is caught, at best, the next night, and anything that account did in between went through. Cost is proportional to the base, so a growing base means a growing bill and a longer run, and the run eventually stops fitting in the night. Rescreening everyone against everything also regenerates the same false positives daily unless you suppress matches already dismissed, and that suppression logic is where mistakes hide.
On list change
Best for A large base that needs same-day coverage without daily cost
Watch the list feeds (or the provider's change notifications), and when an entry is added or amended, screen only the customers whose names or identifiers could match the changed entry.
- Strengths: coverage of the whole base within hours of a change, at a cost that depends on how often lists change rather than how many customers you have. Reviewers see small batches tied to a specific update, which is far easier to work than a morning of unrelated alerts. Dormant customers are covered because the trigger is the list, not the account.
- Costs: you need list versioning, a change feed you trust, and an index of your own customers that can answer which records a changed entry might match; that is a real system to build and to keep correct. It only reacts to list changes, so a customer who changes their own name or adds a new director still needs a per-event trigger. And it is only as good as the delta: a provider that republishes a whole list without a diff forces you back to a full rescreen anyway.
Choosing
Work through the actions first, then the base. The questions below settle it for most products.
flowchart TD
Q1{Must a hit stop an action} -->|Yes| E[Screen per event]:::accent
Q1 -->|No| Q2{Base above tens of thousands}
E --> Q3{Dormant accounts matter}
Q3 -->|Yes| Q2
Q3 -->|No| D[Per event only]
Q2 -->|No| B[Add nightly batch]
Q2 -->|Yes| Q4{Provider offers a change feed}
Q4 -->|Yes| L[Add on list change]
Q4 -->|No| BWhatever branch you end on, keep one screening record per check: who was screened, against which list version, with what match settings, what came back, and who dismissed it and why. Every mode benefits from this, and the on-list-change mode depends on it, because "which customers have we already cleared against this version" is the question it answers hundreds of times a day. It is also what makes a false positive stay dismissed: the same fuzzy match on the same list entry should not return to a reviewer until the entry or the customer changes.
The mistake to avoid is treating this as one choice. The three modes answer different questions: per event asks whether this action is safe now, on list change asks whether yesterday's answer still holds, and batch asks whether the first two missed anything. A product that only has one of them has an answer to one question and a gap where the other two should be. Start with the mode your riskiest action needs, add the mode your base size needs, and let the batch shrink into the role it is good at: a periodic reconciliation, not the front line.