Structuring or Threshold Avoidance
Definition: values engineered to dodge a known limit, repeated.
Construction: Double threshold extensions (IsJustBelowThreshold, IsRoundAmount) combined with
Velocity and Volume or Cardinality for the repetition. Use it when the
risk is evasion of a specific numeric control, and not a generic anomaly, which is
Behavioral Baseline and Ratio. See the Rule Taxonomy.
| Question an analyst asks | Rule Taxonomy type that answers it |
|---|---|
| Are deposits repeatedly just under the 10,000 reporting limit? | Structuring: IsJustBelowThreshold with a TTL Counter for the repetition |
| Is an account always sending round amounts? | Structuring: IsRoundAmount with a TTL Counter for the repetition |
| Are amounts unusually large for this customer? | Not this type: Behavioral Baseline and Ratio |
| Is a single amount just under the limit, once? | Structuring without repetition: a weak signal on its own, see below |
Structuring: The Single Event Signal is Weak
A single amount just below a limit is weak evidence: most such amounts are legitimate. The signal is the repetition, so the rule has two parts. The first is a payload predicate that recognises an engineered value. The second is a counter of how often that has happened for the entity within a window. The two parts are separate Activation Rules, joined by a TTL Counter, following the shape of Velocity and Volume.
Structuring: Construction Steps
Step 1: Recognise the Engineered Value
IsJustBelowThreshold(threshold, marginPercent) is true when the amount is below the threshold and within the margin
percentage of it (IsJustAboveThreshold is the mirror, for a limit that triggers on exceeding it). For a 10,000 limit
with a 10 percent margin, amounts from 9,000 up to but excluding 10,000 are recognised:
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(10000, 10)
IsRoundAmount(nearest) is true when the amount is an exact multiple of the value given, for example 500. Round amounts
are a separate signal, and are often combined with a threshold band, using Require as the guard. See the
Rule Pipeline Reference:
Matched = Payload.CurrencyAmount.RequireIsRoundAmount(500).MatchInRange(5000, 9999)
Where several limits apply, for example 3,000, 10,000 and 15,000,
IsJustBelowAnyThreshold(marginPercent, thresholds...) tests all of them in one step, and takes the margin first:
Matched = Payload.CurrencyAmount.MatchIsJustBelowAnyThreshold(10, 3000, 10000, 15000)
The threshold, margin and rounding unit must come from the data and the control in question, per the Rule Taxonomy, and not from a list of best practice values.
Step 2: Count the Repetition with a TTL Counter
Define a TTL Counter keyed on the entity, windowed to the period the control is measured over (for example Days /
7), and increment it from the Activation Rule of Step 1. The Activation Rule that recognises the engineered value is
the incrementing rule. Priority follows the rules in Velocity and Volume: incrementing earlier
in the Priority order means the current event is included in the count.
Step 3: Alert on the Repetition
Matched = TTLCounter.JustBelowLimit7d.MatchGreaterOrEqual(3)
Where the repetition has to be across different values or destinations, and not merely a count, use an Abstraction Rule with Distinct Count, per Cardinality, for example several just below limit amounts split across many beneficiaries. Where several payments sum past the limit inside a window, use a Sum TTL Counter, and threshold it against the limit directly:
Matched = TTLCounter.DepositValue24h.RequireGreater(10000).Against(TTLCounter.DepositCount24h).RequireGreaterOrEqual(3).Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(10000, 10)
Structuring: Common Faults
Symptoms of a misbuilt Structuring rule, with the likely cause.
| Symptom in a Structuring rule | Likely cause |
|---|---|
| Too many alerts on single events | No repetition condition. A single amount below a limit is weak evidence on its own. |
| Structured amounts just over the margin missed | The margin is too narrow for how the limit is being avoided. Derive the margin from the data. |
| The count includes the alerting event or not | Priority of the incrementing rule relative to the alerting rule. See Velocity and Volume. |
| The limit is a sum across accounts | The counter key is the account, not the beneficial owner. Key the counter on the entity the control applies to. |
Examples by Sub-Class
Validated. Every fragment in this section is compiled individually through the Jube Parser, with its payload fields and its TTL Counter, Abstraction, Calculation, List and Sanction tokens declared, so each extension method and argument type is checked against the engine. Compilation does not validate a rule’s logic or its thresholds, which are illustrative placeholders to be tuned on data.
9.1 Cash structuring (smurfing)
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.1-01 | Cash deposits just below reporting threshold | Deposits in a band just beneath the currency transaction report limit (e.g. 9,000–9,999 where the limit is 10,000). | TTL counter CashDepositBand7d (Data Name AccountId, Interval Days / 7, count only) is incremented by the band Activation Rule at an earlier Priority than the alerting rule, which reads TTLCounter.CashDepositBand7d.MatchGreaterOrEqual(2), so the read includes this deposit. The limit, margin and TxnType value are placeholders. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(10000, 10) |
Check: Limit 10,000 is the US currency transaction report threshold (31 CFR 1010.311); other jurisdictions differ. Verify the limit that applies. Tag: Structuring · AML · Account · 7d, count ≥ 2 in band |
| 9.1-02 | Multiple cash deposits aggregating above threshold within a day | Several sub-threshold deposits whose same-day total exceeds the limit. | TTL sum counter CashBandSum24h (Data Name CustomerId, Interval Hours / 24, Sum on with Data Value CurrencyAmount) is incremented by the band Activation Rule at an earlier Priority, and the alerting rule reads TTLCounter.CashBandSum24h.MatchGreater(10000), so the sum already includes this deposit. The window is rolling, not a business day, and CustomerId is a placeholder key the payload must carry. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(10000, 10) |
Check: Aggregation for the reporting threshold is by business day (US 31 CFR 1010.313), not a rolling 24 hours. Verify. Tag: Structuring · AML · Customer · 24h |
| 9.1-03 | Multi-day cash deposits aggregating above threshold | Rolling sum across consecutive days. | TTL sum counter CashDepositSum5d (Data Name CustomerId, Interval Days / 5, Sum on with Data Value CurrencyAmount) is incremented by an Activation Rule that matches every cash deposit, and the alerting rule reads TTLCounter.CashDepositSum5d.MatchGreater(10000). This is a rolling five-day sum with no band and no consecutive-day test; add the band predicate from 9.1-01 to the incrementing rule to sum sub-threshold deposits only. |
Matched = TTLCounter.CashDepositSum5d.MatchGreater(10000) |
Tag: Structuring · AML · Customer · 5d |
| 9.1-04 | Cash deposits split across branches | Same-day deposits across multiple branches. | Abstraction Rule CashDepositBranches24h (Search Key CustomerId, Search Interval Hours / 24, Function Type Distinct Count on BranchId) over the cash deposits its own Matched fragment selects, and the alerting rule reads Abstraction.CashDepositBranches24h.MatchGreaterOrEqual(2). No amount band is applied here; add the band predicate to the Abstraction’s Matched fragment to narrow it. Confirm whether the current event is in the cached set before fixing the count. |
Matched = Abstraction.CashDepositBranches24h.MatchGreaterOrEqual(2) |
Tag: Structuring · AML · Customer · 24h |
| 9.1-05 | Cash deposits split across accounts | Deposits into several accounts of one person. | Abstraction Rule CashDepositAccounts24h (Search Key CustomerId, Search Interval Hours / 24, Function Type Distinct Count on AccountId) over cash deposits, read by Abstraction.CashDepositAccounts24h.MatchGreaterOrEqual(2). The Search Key must be the person, not the account, or the rule cannot see the person’s other accounts. |
Matched = Abstraction.CashDepositAccounts24h.MatchGreaterOrEqual(2) |
Tag: Structuring · AML · Customer · 24h |
| 9.1-06 | Cash deposits by multiple depositors into one account | Many individuals deposit just below the limit (smurfs). | Abstraction Rule CashBandDepositors7d (Search Key AccountId, Search Interval Days / 7, Function Type Distinct Count on DepositorId, a placeholder field) over the band deposits of 9.1-01, read by Abstraction.CashBandDepositors7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(10000, 10) |
Tag: Structuring · AML · Account · 7d |
| 9.1-07 | Cash deposits split across ATMs | Deposits across several deposit ATMs. | Abstraction Rule CashDepositAtms24h (Search Key CustomerId, Search Interval Hours / 24, Function Type Distinct Count on AtmId) over cash deposits made through the ATM channel, read by Abstraction.CashDepositAtms24h.MatchGreaterOrEqual(2). Channel and AtmId are placeholder field names, and a band predicate can be added to narrow the match. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.Channel).MatchEqual("ATM") |
Tag: Structuring · AML · Customer · 24h |
| 9.1-08 | Cash withdrawals just below reporting limit | Withdrawals in a band under the limit. | TTL counter CashWithdrawBand7d (Data Name AccountId, Interval Days / 7, count only) is incremented by the band Activation Rule at an earlier Priority, and the alerting rule reads TTLCounter.CashWithdrawBand7d.MatchGreaterOrEqual(2). The count of 2 is carried over by analogy with the deposit rule and is not a source figure. |
Matched = Payload.TxnType.RequireEqual("CashWithdrawal").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(10000, 10) |
Check: Withdrawal reporting limit depends on the jurisdiction. Verify. Tag: Structuring · AML · Account · 7d |
| 9.1-09 | Cash withdrawals repeatedly at a daily ATM maximum | Hitting ATM limit on consecutive days. | Abstraction Rule AtmMaxDays7d (Search Key CardId, Search Interval Days / 7, Function Type Distinct Count on BusinessDate, a date-only placeholder field) over withdrawals whose amount is within a band of the daily maximum held in Dictionary AtmDailyMaximum. A distinct-day count equal to the window length means every day in the window hit the maximum, and the alerting rule reads Abstraction.AtmMaxDays7d.MatchGreaterOrEqual(7). |
Matched = Payload.TxnType.RequireEqual("CashWithdrawal").Against(Payload.CurrencyAmount).MatchRatioInRange(Dictionary.AtmDailyMaximum, 0.999, 1) |
Check: ATM daily maximums vary by issuer and operator. Verify the Dictionary value. Tag: Structuring · Both · Card · 7d |
| 9.1-10 | Cash purchases of monetary instruments just below the record-keeping threshold | Money orders/cashier’s cheques just under limit. | TTL counter MonetaryInstrumentBand7d (Data Name CustomerId, Interval Days / 7, count only) is incremented by the band Activation Rule at an earlier Priority, and the alerting rule reads TTLCounter.MonetaryInstrumentBand7d.MatchGreaterOrEqual(2). This is a record-keeping limit, not a reporting limit, and the margin is a tuning choice. |
Matched = Payload.TxnType.RequireEqual("MonetaryInstrumentPurchase").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(3000, 10) |
Check: US record-keeping threshold for money orders and cashier’s cheques is USD 3,000 (31 CFR 1010.415). Verify the value and the band margin for the regime. Tag: Structuring · AML · Customer · 7d |
| 9.1-11 | Foreign currency exchange just below ID threshold | FX transactions beneath customer identification limit. | TTL counter FxBand7d (Data Name CustomerId, Interval Days / 7, count only) is incremented by the band Activation Rule at an earlier Priority, and the alerting rule reads TTLCounter.FxBand7d.MatchGreaterOrEqual(2). The identification threshold is the Dictionary FxIdThreshold, so it can change without a redeploy. |
Matched = Payload.TxnType.RequireEqual("FxExchange").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.FxIdThreshold, 10) |
Check: FX identification thresholds differ by jurisdiction and product. Verify the Dictionary value. Tag: Structuring · AML · Customer · 7d |
| 9.1-12 | Cash deposits in round just-below amounts (e.g. 4,900/ 9,900) | Round-number bands. | TTL counter RoundBand14d (Data Name AccountId, Interval Days / 14, count only) is incremented by the cash deposit rule at an earlier Priority, which requires the amount to be a multiple of the rounding unit and to sit just below either of two illustrative limits. The alerting rule reads TTLCounter.RoundBand14d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.CurrencyAmount).RequireIsRoundAmount(100).MatchIsJustBelowAnyThreshold(2, 5000, 10000) |
Tag: Structuring · AML · Account · 14d |
| 9.1-13 | Deposit followed by same-day cash withdrawal below limit | Cash in and out both under thresholds. | Two TTL counters keyed on AccountId with Interval Hours / 24, count only: CashIn24h, incremented by deposits under the limit, and CashOut24h, incremented by withdrawals under the limit. The alerting rule reads both, so it fires when each occurred in the window. Order (deposit before withdrawal) is not enforced; use an Abstraction with a First or Last offset if order matters. |
Matched = TTLCounter.CashIn24h.RequireGreaterOrEqual(1).Against(TTLCounter.CashOut24h).MatchGreaterOrEqual(1) |
Tag: Structuring · AML · Account · 24h |
| 9.1-16 | Deposits timed just after midnight cut-off | Split across business days to reset counters. | TTL counter MidnightDeposits3d (Data Name AccountId, Interval Days / 3, count only) is incremented by cash deposits whose hour is the first hour after midnight, and the alerting rule reads TTLCounter.MidnightDeposits3d.MatchGreaterOrEqual(3), where the count is a placeholder. The cut-off hour and width are design choices, and ToZone is needed first if the payload clock is not the bank’s business-day clock. TransactionTime is a placeholder field name. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.TransactionTime).HourOfDay().MatchInRange(0, 0) |
Tag: Structuring · AML · Account · 3d |
9.2 Wire, transfer and payment limit avoidance
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.2-01 | Payments just below transaction monitoring/review threshold | Payments in a band under the review limit. | TTL counter ReviewBand7d (Data Name AccountId, Interval Days / 7, count only) is incremented by the band predicate at an earlier Priority than the alerting rule, which reads TTLCounter.ReviewBand7d.MatchGreaterOrEqual(2). The review limit is Dictionary ReviewThreshold, a number per key where a missing key reads zero and so leaves the band empty. A single fixed limit can be an inline literal instead. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ReviewThreshold, 10) |
Tag: Structuring · Both · Account · 7d |
| 9.2-02 | Payments just below the SCA/step-up authentication limit | Amounts below strong-authentication exemption thresholds (e.g. low-value exemption). | Two TTL counters keyed on CardId with Interval Hours / 24, both incremented by the band predicate: ScaLowValueCount24h (count) and ScaLowValueSum24h (Sum on CurrencyAmount). The alerting rule reads the cumulative value with TTLCounter.ScaLowValueSum24h.MatchGreater(100). The exemption is cumulative since the last strong authentication, so the 24-hour window approximates it, and a count read on ScaLowValueCount24h reproduces the consecutive-transaction limit more closely. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(30, 10) |
Check: PSD2 RTS Article 16 low-value exemption is EUR 30 per transaction, with cumulative EUR 100 or five consecutive transactions. Verify the current text and national implementation. Tag: Structuring · Fraud · Account · 24h |
| 9.2-03 | Payments just below daily limit repeated daily | Hitting limit on successive days. | Abstraction Rule DailyLimitDays7d (Search Key AccountId, Search Interval Days / 7, Function Type Distinct Count on BusinessDate, a date-only placeholder field) over payments whose amount is within a band of the DailyLimit payload field. A distinct-day count equal to the window length means every day hit the band, read by Abstraction.DailyLimitDays7d.MatchGreaterOrEqual(7). |
Matched = Payload.CurrencyAmount.MatchRatioInRange(Payload.DailyLimit, 0.9, 0.9999) |
Tag: Structuring · Fraud · Account · 7d |
| 9.2-04 | Payments just below dual-approval or four-eyes threshold | Under corporate approval limits. | TTL counter DualApprovalBand30d (Data Name UserId, Interval Days / 30, count only) is incremented by the band predicate, with Dictionary DualApprovalLimit as the approval limit, and the alerting rule reads TTLCounter.DualApprovalBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.DualApprovalLimit, 10) |
Tag: Structuring · Fraud · Account · 30d |
| 9.2-05 | Payments just below purchase-order threshold (vendor fraud) | Invoices under procurement approval limits. | TTL counter PoBand30d (Data Name VendorId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary PurchaseOrderLimit, and the alerting rule reads TTLCounter.PoBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. Keying on the vendor shows the pattern across requesters. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.PurchaseOrderLimit, 10) |
Tag: Structuring · Fraud · Vendor · 30d |
| 9.2-06 | Invoice splitting | One invoice split across several to remain beneath approval limits. | TTL sum counter InvoiceBandSum30d (Data Name VendorId, Interval Days / 30, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary ApprovalLimit, and the alerting rule reads TTLCounter.InvoiceBandSum30d.MatchGreater(Dictionary.ApprovalLimit). Keying on the invoice number, if the payload carries it, tests one invoice exactly, while keying on the vendor over-groups invoices from the same vendor. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ApprovalLimit, 10) |
Tag: Structuring · Fraud · Vendor · 30d |
| 9.2-07 | Expense claims just below receipt requirement | Claims in a band under the receipt threshold. | TTL counter ExpenseBand30d (Data Name EmployeeId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary ReceiptThreshold, and the alerting rule reads TTLCounter.ExpenseBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. ExpenseAmount is a placeholder field name. |
Matched = Payload.ExpenseAmount.MatchIsJustBelowThreshold(Dictionary.ReceiptThreshold, 10) |
Check: Receipt requirements are set by company policy and tax rules per jurisdiction. Verify the Dictionary value. Tag: Structuring · Fraud · Employee · 30d |
| 9.2-08 | Cross-border transfers just below reporting limit | International transfers under the declarable amount. | TTL counter CrossBorderBand30d (Data Name CustomerId, Interval Days / 30, count only) is incremented by a rule that requires the destination country to differ from the origin country and the amount to sit just below Dictionary DeclarableAmount, and the alerting rule reads TTLCounter.CrossBorderBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. The country field names are placeholders. |
Matched = Payload.DestinationCountry.RequireNotEqual(Payload.OriginCountry).Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.DeclarableAmount, 10) |
Check: The declarable amount for international transfers differs by jurisdiction, and some regimes report every transfer. Verify the Dictionary value. Tag: Structuring · AML · Customer · 30d |
| 9.2-09 | Splitting a wire into multiple smaller wires same day | Same beneficiary, same day splits. | Abstraction Rule SameBeneficiaryBand24h (Search Key AccountId, Search Interval Hours / 24, Function Type Same Count on BeneficiaryId) over wires in the band predicate with Dictionary WireReviewLimit. Same Count is used because the test is repetition to one beneficiary, which a counter keyed on the account cannot express, and the alerting rule reads Abstraction.SameBeneficiaryBand24h.MatchGreaterOrEqual(1), meaning at least one earlier same-beneficiary band wire. Confirm the current event is excluded from the cached set. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.WireReviewLimit, 10) |
Tag: Structuring · AML · Account · 24h |
| 9.2-10 | Splitting across multiple originating accounts to one beneficiary | Multiple sources of sub-threshold payments to one payee. | Abstraction Rule BeneficiaryOriginAccounts24h (Search Key BeneficiaryId, Search Interval Hours / 24, Function Type Distinct Count on AccountId) over wires in the band predicate with Dictionary WireReviewLimit, read by Abstraction.BeneficiaryOriginAccounts24h.MatchGreaterOrEqual(2). Keying on the beneficiary groups several originating accounts paying one payee. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.WireReviewLimit, 10) |
Tag: Structuring · AML · Beneficiary · 24h |
| 9.2-11 | Payments just below sanctions-screening or trade-licence thresholds | Value-based screening triggers avoided. | TTL counter ScreeningBand30d (Data Name CustomerId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary ScreeningThreshold, and the alerting rule reads TTLCounter.ScreeningBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. The screening and trade-licence values are Dictionary entries. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ScreeningThreshold, 10) |
Tag: Structuring · AML · Customer · 30d |
| 9.2-12 | Payments just below crypto travel-rule threshold | Transfers kept under the travel-rule limit. | TTL counter TravelRuleBand7d (Data Name CustomerId, Interval Days / 7, count only) is incremented by crypto transfers just below Dictionary TravelRuleThreshold, and the alerting rule reads TTLCounter.TravelRuleBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. The value is a Dictionary entry so it can follow the regime. |
Matched = Payload.TxnType.RequireEqual("CryptoTransfer").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.TravelRuleThreshold, 10) |
Check: FATF Recommendation 16 guidance uses USD or EUR 1,000 for virtual asset transfers. Some jurisdictions use other values, for example USD 3,000 under the US recordkeeping rule. Verify the value that applies. Tag: Structuring · AML · Customer · 7d |
| 9.2-13 | Payments just below KYC tier limits | Staying within lower-verification tier. | TTL counter KycTierBand30d (Data Name AccountId, Interval Days / 30, count only) is incremented by the band predicate, with the tier limit read from the payload as KycTierLimit, so the limit follows the account’s tier. The alerting rule reads TTLCounter.KycTierBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Payload.KycTierLimit, 10) |
Tag: Structuring · AML · Account · 30d |
| 9.2-14 | Payments just below wallet tier limit | E-money limits for unverified wallets. | TTL counter WalletTierBand30d (Data Name WalletId, Interval Days / 30, count only) is incremented by the band predicate, with the wallet tier limit read from the payload as WalletTierLimit, and the alerting rule reads TTLCounter.WalletTierBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Payload.WalletTierLimit, 10) |
Check: E-money limits for unverified wallets differ by jurisdiction. Verify the value. Tag: Structuring · AML · Wallet · 30d |
| 9.2-15 | Repeated top-ups just under load limit | Prepaid reload under limit. | TTL counter ReloadBand30d (Data Name CardId, Interval Days / 30, count only) is incremented by top-up transactions just below Dictionary LoadLimit, and the alerting rule reads TTLCounter.ReloadBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. The TopUp type value is a placeholder name. |
Matched = Payload.TxnType.RequireEqual("TopUp").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.LoadLimit, 10) |
Tag: Structuring · AML · Card · 30d |
| 9.2-16 | Remittances just below ID limit | Remittance amounts below identification threshold. | TTL counter RemittanceBand7d (Data Name SenderId, Interval Days / 7, count only) is incremented by remittances just below Dictionary RemittanceIdLimit, and the alerting rule reads TTLCounter.RemittanceBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("Remittance").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.RemittanceIdLimit, 10) |
Check: Remittance identification thresholds vary by jurisdiction. Verify the Dictionary value. Tag: Structuring · AML · Sender · 7d |
| 9.2-17 | Remittance splitting across agents | Same sender splits across agents. | Abstraction Rule RemittanceAgents24h (Search Key SenderId, Search Interval Hours / 24, Function Type Distinct Count on AgentId, a placeholder field) over remittances in the band predicate with Dictionary RemittanceIdLimit, read by Abstraction.RemittanceAgents24h.MatchGreaterOrEqual(2). |
Matched = Payload.TxnType.RequireEqual("Remittance").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.RemittanceIdLimit, 10) |
Tag: Structuring · AML · Sender · 24h |
| 9.2-18 | Split sends to many beneficiaries just below limit (smurfing via remittance) | Many senders and recipients under the cap. | Abstraction Rule SenderBeneficiaries7d (Search Key SenderId, Search Interval Days / 7, Function Type Distinct Count on BeneficiaryId) over remittances in the band predicate with Dictionary RemittanceIdLimit, read by Abstraction.SenderBeneficiaries7d.MatchGreaterOrEqual(3), where the count is a placeholder. The key is the sender, the subject of the title. The library’s key label says Beneficiary, which conflicts with the title, so confirm the intended key. |
Matched = Payload.TxnType.RequireEqual("Remittance").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.RemittanceIdLimit, 10) |
Tag: Structuring · AML · Beneficiary · 7d |
9.3 Card, merchant and acquiring threshold avoidance
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.3-01 | Transactions just below floor limit/offline limit | Amounts beneath authorisation floor. | TTL counter FloorLimitBand24h (Data Name MerchantId, Interval Hours / 24, count only) is incremented by the band predicate with Dictionary FloorLimit, and the alerting rule reads TTLCounter.FloorLimitBand24h.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.FloorLimit, 10) |
Check: Floor and offline limits are set by scheme, acquirer or issuer and vary by market. Verify the value used. Tag: Structuring · Fraud · Merchant · 24h |
| 9.3-02 | Transactions just below contactless limit repeated | Repeated contactless just under cap (stolen card). | TTL counter ContactlessBand2h (Data Name CardId, Interval Hours / 2, count only) is incremented by the band predicate with Dictionary ContactlessLimit, and the alerting rule reads TTLCounter.ContactlessBand2h.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ContactlessLimit, 10) |
Check: Contactless no-cardholder-verification limits differ by country and change over time. The UK limit has been GBP 100 since 2021 as far as I know. Verify. Tag: Structuring · Fraud · Card · 2h |
| 9.3-03 | Transactions just below PIN-required limit | Under verification limit. | TTL counter PinBand2h (Data Name CardId, Interval Hours / 2, count only) is incremented by the band predicate with Dictionary PinLimit, and the alerting rule reads TTLCounter.PinBand2h.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.PinLimit, 10) |
Check: PIN-free limits vary by country and issuer. Verify the Dictionary value. Tag: Structuring · Fraud · Card · 2h |
| 9.3-04 | Transaction splitting at merchant (split tender) | One purchase split to avoid authorisation limits. | TTL counter SplitTenderBand1h (Data Name MerchantId, Interval Hours / 1, count only) is incremented by the band predicate with Dictionary AuthorisationLimit, and the alerting rule reads TTLCounter.SplitTenderBand1h.MatchGreaterOrEqual(2). It is keyed on the merchant, so one card across merchants is not flagged. Confirming one purchase split needs a card-level second rule. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.AuthorisationLimit, 10) |
Tag: Structuring · Fraud · Merchant · 1h |
| 9.3-05 | Transaction laundering at merchant just under velocity limits | Charges kept under merchant limits. | TTL sum counter MerchantSum7d (Data Name MerchantId, Interval Days / 7, Sum on CurrencyAmount) is incremented by an Activation Rule that matches every sale, and the alerting rule tests whether the running merchant total sits just under the Dictionary MerchantVelocityLimit. The 5 percent margin is a tuning value. |
Matched = TTLCounter.MerchantSum7d.MatchIsJustBelowThreshold(Dictionary.MerchantVelocityLimit, 5) |
Tag: Structuring · Fraud · Merchant · 7d |
| 9.3-06 | Factoring (third-party processing) just below per-transaction caps | Hidden merchants processing in small amounts. | TTL counter FactoringBand7d (Data Name MerchantId, Interval Days / 7, count only) is incremented by the band predicate with Dictionary PerTransactionCap, and only sales whose descriptor is dissimilar to the registered name get that far, because the rejecting step decides before the band is read. The 0.8 similarity cut-off is a tuning value, and a mismatch is a signal, not proof of factoring. The alerting rule reads TTLCounter.FactoringBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.DescriptorName.RejectSimilarityAtLeast(Payload.RegisteredName, 0.8).Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.PerTransactionCap, 10) |
Tag: Structuring · Fraud · Merchant · 7d |
| 9.3-07 | Refund amounts just below approval threshold | Refunds under supervisor approval. | TTL counter RefundBand7d (Data Name EmployeeId, the employee who processed the refund, Interval Days / 7, count only) is incremented by refunds just below Dictionary RefundApprovalLimit, and the alerting rule reads TTLCounter.RefundBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("Refund").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.RefundApprovalLimit, 10) |
Tag: Structuring · Fraud · Employee · 7d |
| 9.3-08 | Voids just below manager approval | Voids under limits. | TTL counter VoidBand7d (Data Name EmployeeId, Interval Days / 7, count only) is incremented by voids just below Dictionary VoidApprovalLimit, and the alerting rule reads TTLCounter.VoidBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. The Void type value is a placeholder name. |
Matched = Payload.TxnType.RequireEqual("Void").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.VoidApprovalLimit, 10) |
Tag: Structuring · Fraud · Employee · 7d |
| 9.3-09 | Gift card sales just below KYC/ID threshold | Gift card purchases under limits. | TTL counter GiftCardBand7d (Data Name CustomerId, Interval Days / 7, count only) is incremented by gift card sales just below Dictionary GiftCardKycLimit, and the alerting rule reads TTLCounter.GiftCardBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("GiftCardSale").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.GiftCardKycLimit, 10) |
Check: Gift card KYC and ID limits vary by jurisdiction and issuer. Verify the Dictionary value. Tag: Structuring · AML · Customer · 7d |
| 9.3-10 | Chargeback ratio kept just under programme threshold | Merchants managing chargebacks under thresholds (e.g. by swapping MIDs). | Two TTL counters keyed on MerchantId with Interval Days / 90, count only: Chargebacks90d, incremented by chargeback transactions, and Transactions90d, incremented by sales. The alerting rule reads the ratio of the two and tests it just below Dictionary SchemeChargebackRatio. Keying on the merchant principal instead closes the gap that swapping MIDs opens, and needs a principal key in the payload. |
Matched = TTLCounter.Chargebacks90d.RatioOf(TTLCounter.Transactions90d).MatchIsJustBelowThreshold(Dictionary.SchemeChargebackRatio, 10) |
Check: Scheme chargeback monitoring thresholds and their current values change. Verify the Dictionary value against the current scheme rules. Tag: Structuring · Fraud · Merchant · 90d |
| 9.3-11 | MID cycling to dodge ratio monitoring | Opening and shifting volume across MIDs. | Abstraction Rule OwnerMids90d (Search Key OwnerId, Search Interval Days / 90, Function Type Distinct Count on MerchantId) over sales, read by Abstraction.OwnerMids90d.MatchGreaterOrEqual(3), where the count is a placeholder. OwnerId is a placeholder principal key that needs a KYB linkage in the payload, because without it one owner cycling MIDs is invisible. |
Matched = Abstraction.OwnerMids90d.MatchGreaterOrEqual(3) |
Tag: Structuring · Fraud · Merchant · 90d |
9.4 Credit, lending and application threshold avoidance
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.4-01 | Loan applications just below enhanced-verification amount | Amounts below documentation thresholds. | TTL counter LoanBand30d (Data Name ApplicantId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary EnhancedVerificationAmount, and the alerting rule reads TTLCounter.LoanBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. RequestedAmount is a placeholder field name. |
Matched = Payload.RequestedAmount.MatchIsJustBelowThreshold(Dictionary.EnhancedVerificationAmount, 10) |
Tag: Structuring · Fraud · Applicant · 30d |
| 9.4-02 | Multiple small loans instead of one large | Loans split under limits. | TTL sum counter SmallLoanSum30d (Data Name ApplicantId, Interval Days / 30, Sum on RequestedAmount) is incremented by the band predicate with Dictionary LoanDocumentationLimit, and the alerting rule reads TTLCounter.SmallLoanSum30d.MatchGreater(Dictionary.LoanDocumentationLimit), flagging when sub-limit loans together reach the amount one loan would trigger. |
Matched = Payload.RequestedAmount.MatchIsJustBelowThreshold(Dictionary.LoanDocumentationLimit, 10) |
Tag: Structuring · Fraud · Applicant · 30d |
| 9.4-03 | Income declared just above qualification threshold | Declared income clusters around cut-off. | Single event, no counter: the declared income arrives as text, so ParseDouble() converts it before the predicate tests it against Dictionary QualificationIncome with a placeholder margin. Clustering across applicants is a TTL counter keyed on IntermediaryId over 90 days, incremented by the same predicate, and only matters when the cluster does. |
Matched = Payload.DeclaredIncome.ParseDouble().MatchIsJustAboveThreshold(Dictionary.QualificationIncome, 5) |
Tag: Structuring · Fraud · Application · single event |
| 9.4-04 | Deposit amounts just above minimum balance for bonus | Bonus/incentive abuse. | TTL counter BonusBand30d (Data Name AccountId, Interval Days / 30, count only) is incremented by the predicate that tests the deposit just above Dictionary BonusMinimum, and the alerting rule reads TTLCounter.BonusBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. DepositAmount is a placeholder field name. |
Matched = Payload.DepositAmount.MatchIsJustAboveThreshold(Dictionary.BonusMinimum, 5) |
Tag: Structuring · Fraud · Account · 30d |
| 9.4-05 | Promotion abuse: multiple accounts each just meeting minimum | Many accounts with minimum qualification. | Abstraction Rule PromoAccountsByDevice30d (Search Key DeviceId, Search Interval Days / 30, Function Type Distinct Count on AccountId) over accounts whose balance sits just above Dictionary PromoMinimum, read by Abstraction.PromoAccountsByDevice30d.MatchGreaterOrEqual(3), where the count is a placeholder. The device key groups accounts opened from one device. Balance is a placeholder field name. |
Matched = Payload.Balance.MatchIsJustAboveThreshold(Dictionary.PromoMinimum, 5) |
Tag: Structuring · Fraud · Device · 30d |
| 9.4-06 | Credit limit increase requests just below automatic-review level | Incremental requests. | TTL counter CliBand60d (Data Name AccountId, Interval Days / 60, count only) is incremented by requested limits just below Dictionary AutoReviewLevel, and the alerting rule reads TTLCounter.CliBand60d.MatchGreaterOrEqual(3), where the count is a placeholder. RequestedLimit is a placeholder field name. |
Matched = Payload.RequestedLimit.MatchIsJustBelowThreshold(Dictionary.AutoReviewLevel, 10) |
Tag: Structuring · Fraud · Account · 60d |
9.5 Trade, securities and other thresholds
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.5-01 | Invoice values just below customs declaration threshold | Under-declaration at borders. | TTL counter CustomsBand30d (Data Name TraderId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary CustomsThreshold, and the alerting rule reads TTLCounter.CustomsBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. InvoiceValue is a placeholder field name. |
Matched = Payload.InvoiceValue.MatchIsJustBelowThreshold(Dictionary.CustomsThreshold, 10) |
Check: Customs declaration thresholds are set by each customs authority. Verify the Dictionary value. Tag: Structuring · AML · Trade · 30d |
| 9.5-02 | Shipments split to remain under de minimis | Parcels split under duty-free values. | TTL sum counter ShipmentBandSum30d (Data Name RecipientId, Interval Days / 30, Sum on ShipmentValue) is incremented by the band predicate with Dictionary DeMinimis, and the alerting rule reads TTLCounter.ShipmentBandSum30d.MatchGreater(Dictionary.DeMinimis), so splits are caught when the sum to one recipient crosses the value. |
Matched = Payload.ShipmentValue.MatchIsJustBelowThreshold(Dictionary.DeMinimis, 10) |
Check: De minimis values for duty-free import vary by jurisdiction and have been under review. Verify the current value. Tag: Structuring · AML · Trade · 30d |
| 9.5-03 | Securities purchases just below disclosure threshold | Stakeholding accumulations beneath reporting. | TTL sum counter SharesSum30d keyed on BeneficialOwnerIssuerKey, a composite the payload must carry (Interval Days / 30, Sum on SharesAcquired), is incremented by an Activation Rule that matches every acquisition. The alerting rule tests the ratio of that sum to Dictionary SharesInIssue, which must be held per issuer, just below a placeholder level. Keying on the account would sum across issuers and owners. |
Matched = TTLCounter.SharesSum30d.RatioOf(Dictionary.SharesInIssue).MatchIsJustBelowThreshold(0.05, 5) |
Check: Disclosure thresholds differ by regime (3% and 5% are common levels). Verify the threshold and whether it counts all holdings or voting rights only. Tag: Structuring · AML · Account · 30d |
| 9.5-04 | Insurance premium payments just below cash reporting | Premiums in cash beneath limit. | TTL counter PremiumCashBand30d (Data Name PolicyId, Interval Days / 30, count only) is incremented by premium cash payments just below Dictionary CashReportingLimit, and the alerting rule reads TTLCounter.PremiumCashBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. The PremiumCash type value is a placeholder name. |
Matched = Payload.TxnType.RequireEqual("PremiumCash").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.CashReportingLimit, 10) |
Check: Cash reporting limits for premiums vary by jurisdiction. Verify the Dictionary value. Tag: Structuring · AML · Policy · 30d |
| 9.5-05 | Real-estate payments split to avoid reporting | Cash or third-party splits. | TTL sum counter PropertyBandSum30d (Data Name PropertyId, a placeholder field, Interval Days / 30, Sum on CurrencyAmount) is incremented by property payments just below Dictionary ReportingLimit, and the alerting rule reads TTLCounter.PropertyBandSum30d.MatchGreater(Dictionary.ReportingLimit), so cash or third-party splits on one purchase sum together. |
Matched = Payload.TxnType.RequireEqual("PropertyPayment").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.ReportingLimit, 10) |
Check: Real-estate reporting obligations (for example the US FinCEN residential real-estate rule) have specific scope, effective dates and thresholds. Verify before relying on them. Tag: Structuring · AML · Account · 30d |
| 9.5-06 | Casino buy-ins just below ID threshold | Chip purchases beneath ID limits. | TTL counter BuyInBand24h (Data Name PlayerId, Interval Hours / 24, count only) is incremented by casino buy-ins just below Dictionary CasinoIdThreshold, and the alerting rule reads TTLCounter.BuyInBand24h.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("CasinoBuyIn").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.CasinoIdThreshold, 10) |
Check: Casino reporting and ID thresholds differ by jurisdiction. The US casino CTR is USD 10,000 (31 CFR 1021.311). Verify the Dictionary value. Tag: Structuring · AML · Player · 24h |
| 9.5-07 | Casino cash-outs just below reporting | Redemptions under limit. | TTL counter CashOutBand24h (Data Name PlayerId, Interval Hours / 24, count only) is incremented by casino cash-outs just below Dictionary CasinoReportingLimit, and the alerting rule reads TTLCounter.CashOutBand24h.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("CasinoCashOut").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.CasinoReportingLimit, 10) |
Check: Same casino reporting caveat as the buy-in row. Verify the Dictionary value. Tag: Structuring · AML · Player · 24h |
| 9.5-08 | Virtual asset purchases just below exchange KYC limits | Crypto buys kept under tier limits. | TTL counter CryptoKycBand7d (Data Name CustomerId, Interval Days / 7, count only) is incremented by crypto purchases just below Dictionary KycTierLimit, and the alerting rule reads TTLCounter.CryptoKycBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("CryptoPurchase").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.KycTierLimit, 10) |
Tag: Structuring · AML · Customer · 7d |
| 9.5-09 | Crypto withdrawals just below withdrawal-review limits | Splitting withdrawals. | TTL counter CryptoWithdrawBand7d (Data Name CustomerId, Interval Days / 7, count only) is incremented by crypto withdrawals just below Dictionary WithdrawalReviewLimit, and the alerting rule reads TTLCounter.CryptoWithdrawBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.TxnType.RequireEqual("CryptoWithdrawal").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.WithdrawalReviewLimit, 10) |
Tag: Structuring · AML · Customer · 7d |
| 9.5-10 | Donations just below disclosure threshold | Charity/political donations split. | TTL sum counter DonationBandSum30d (Data Name DonorId, Interval Days / 30, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary DonationDisclosureLevel, and the alerting rule reads TTLCounter.DonationBandSum30d.MatchGreater(Dictionary.DonationDisclosureLevel), summing split donations to one donor against the level. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.DonationDisclosureLevel, 10) |
Check: Donor disclosure levels differ widely between charity and political regimes. Verify the Dictionary value. Tag: Structuring · AML · Donor · 30d |
| 9.5-11 | Tax-related split payments just below reporting | Contractor payments just under 1099-style thresholds. | TTL sum counter PayeeBandSum12m (Data Name PayeeId, the contractor the annual reporting applies to, Interval Months / 12, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary ReportingThreshold, and the alerting rule reads TTLCounter.PayeeBandSum12m.MatchGreater(Dictionary.ReportingThreshold). The library’s key label says Payer, which conflicts with the title, so confirm the intended key. A month-length window needs a month interval type on the counter, and availability should be confirmed. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ReportingThreshold, 10) |
Check: US Form 1099 reporting thresholds are set per tax year and have changed. Verify the current-year value and any other jurisdiction’s equivalent. Tag: Structuring · AML · Payer · 12m |
9.6 Behavioural signs of evasion
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.6-03 | Declined transaction followed by lower amount retry | Retries below limit after decline. | Abstraction Rule MaxDeclinedAmount1h (Search Key AccountId, Search Interval Hours / 1, Function Type Max on CurrencyAmount) over declined payments, where Status is a placeholder field and Declined a placeholder value. The retry is below the largest declined attempt in the hour and below Dictionary RetryLimit. Confirm the current declined attempt is in the cache before the comparison is read. |
Matched = Payload.CurrencyAmount.RequireLess(Abstraction.MaxDeclinedAmount1h).MatchLess(Dictionary.RetryLimit) |
Tag: Structuring · Fraud · Account · 1h |
| 9.6-04 | Repeated amounts just below limit across different channels | Using ATM, branch, online each under limit. | Abstraction Rule BandChannels24h (Search Key CustomerId, Search Interval Hours / 24, Function Type Distinct Count on Channel, a placeholder field) over payments in the band predicate with Dictionary ReportingLimit, read by Abstraction.BandChannels24h.MatchGreaterOrEqual(3). Three channels is the source’s example and the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ReportingLimit, 10) |
Tag: Structuring · AML · Customer · 24h |
| 9.6-05 | Same just-below amount used across multiple accounts of linked customers | Linked parties echo amounts. | Abstraction Rule SameBandAmountDevice7d (Search Key DeviceId, Search Interval Days / 7, Function Type Same Count on CurrencyAmount) over payments in the band predicate with Dictionary ReportingLimit, read by Abstraction.SameBandAmountDevice7d.MatchGreaterOrEqual(1). The device key stands in for linked customers, and multi-hop links are graph computation, which routes to GraphDirectedWorkflows. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ReportingLimit, 10) |
Tag: Structuring · AML · Cluster · 7d |
| 9.6-06 | Sudden change from large single deposits to many small ones | Pattern switch after threshold or review. | Two Abstraction Rules keyed on AccountId: LargeDepositCount60d (Search Interval Days / 60, Function Type Count over cash deposits at or above Dictionary LargeDepositLevel) and SmallDepositCount14d (Search Interval Days / 14, Function Type Count over cash deposits below Dictionary SmallDepositLevel). The alerting rule requires at least five small deposits in the shorter window and at least one large deposit in the longer one. The windows overlap, so this tests some large and many small deposits in 60 days, not a strict before and after; a First or Skip First offset on the large-deposit rule gives strict ordering if needed. The level values are tuning choices. |
Matched = Payload.TxnType.RequireEqual("CashDeposit").Against(Payload.CurrencyAmount).MatchLess(Dictionary.SmallDepositLevel) |
Tag: Structuring · AML · Account · 60d |
9.7 Sector-specific threshold avoidance
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.7-01 | Claims kept just below investigation threshold (insurance) | Claim amounts cluster beneath referral level. | TTL counter ClaimBand24m (Data Name ClaimantId, Interval in months, count only) is incremented by the band predicate with Dictionary ReferralLevel, and the alerting rule reads TTLCounter.ClaimBand24m.MatchGreaterOrEqual(3), where the count is a placeholder. A 24-month window needs a month interval type on the counter, which should be confirmed. ClaimAmount is a placeholder field name. |
Matched = Payload.ClaimAmount.MatchIsJustBelowThreshold(Dictionary.ReferralLevel, 10) |
Tag: Structuring · Fraud · Claimant · 24m |
| 9.7-02 | Claims split across policies to stay under limits | Same loss claimed in parts. | TTL sum counter ClaimSumByLoss12m (Data Name IncidentId, a key the payload must carry, Interval in months, Sum on ClaimAmount) is incremented by the band predicate with Dictionary ClaimLimit, and the alerting rule reads TTLCounter.ClaimSumByLoss12m.MatchGreater(Dictionary.ClaimLimit). Keying on the incident sums one loss claimed under several policies, and a claimant key would mix unrelated losses. |
Matched = Payload.ClaimAmount.MatchIsJustBelowThreshold(Dictionary.ClaimLimit, 10) |
Tag: Structuring · Fraud · Claimant · 12m |
| 9.7-03 | Premiums paid in cash just below cash threshold | Cash premium structuring. | TTL counter PremiumCashBand12m (Data Name PolicyId, Interval in months, count only) is incremented by premium cash payments just below Dictionary CashThreshold, and the alerting rule reads TTLCounter.PremiumCashBand12m.MatchGreaterOrEqual(3), where the count is a placeholder. It shares the cash-premium shape with 9.5-04, on a 12-month window. |
Matched = Payload.TxnType.RequireEqual("PremiumCash").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.CashThreshold, 10) |
Check: Cash premium thresholds vary by jurisdiction. Verify the Dictionary value. Tag: Structuring · AML · Policy · 12m |
| 9.7-04 | Deposits just below gaming operator’s source-of-funds check level | Affordability threshold avoidance. | TTL counter SofBand30d (Data Name PlayerId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary SourceOfFundsLevel, and the alerting rule reads TTLCounter.SofBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.SourceOfFundsLevel, 10) |
Tag: Structuring · Both · Player · 30d |
| 9.7-05 | Withdrawals split under the operator’s manual-review limit | Withdrawal splitting. | TTL counter WithdrawBand7d (Data Name PlayerId, Interval Days / 7, count only) is incremented by withdrawals just below Dictionary ManualReviewLimit, and the alerting rule reads TTLCounter.WithdrawBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. The Withdrawal type value is a placeholder name. |
Matched = Payload.TxnType.RequireEqual("Withdrawal").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.ManualReviewLimit, 10) |
Tag: Structuring · AML · Player · 7d |
| 9.7-06 | Chip purchases per visit just under reporting | Casino CTR avoidance. | TTL sum counter VisitBandSum24h (Data Name VisitId, Interval Hours / 24, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary CasinoReportingLimit, and the alerting rule reads TTLCounter.VisitBandSum24h.MatchGreater(Dictionary.CasinoReportingLimit). A visit has no fixed length, so a 24-hour window keyed on the visit approximates it, and a visit-end event would close it exactly. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.CasinoReportingLimit, 10) |
Check: Casino reporting threshold differs by jurisdiction. Verify the Dictionary value. Tag: Structuring · AML · Player · visit |
| 9.7-07 | Crypto purchases kept under enhanced-KYC tier | Tier gaming. | TTL counter CryptoTierBand30d (Data Name AccountId, Interval Days / 30, count only) is incremented by the band predicate with Dictionary EnhancedKycTier, and the alerting rule reads TTLCounter.CryptoTierBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.EnhancedKycTier, 10) |
Tag: Structuring · AML · Account · 30d |
| 9.7-08 | On-chain transfers just below travel-rule value | Repeated sub-threshold transfers to same address. | TTL counter OnChainBand7d (Data Name DestinationAddress, a placeholder field, Interval Days / 7, count only) is incremented by on-chain transfers just below Dictionary TravelRuleValue, and the alerting rule reads TTLCounter.OnChainBand7d.MatchGreaterOrEqual(3), where the count is a placeholder. Keying on the destination address, not the account as the source’s label says, makes repeated sub-threshold transfers to one address the signal. |
Matched = Payload.TxnType.RequireEqual("OnChainTransfer").Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(Dictionary.TravelRuleValue, 10) |
Check: FATF travel-rule guidance uses USD or EUR 1,000 for virtual assets. National values differ. Verify. Tag: Structuring · AML · Account · 7d |
| 9.7-09 | Mobile-money transactions at the per-transaction cap repeatedly | Cap-hugging. | TTL counter CapHitCount7d (Data Name WalletId, Interval Days / 7, count only) is incremented by transactions exactly equal to Dictionary PerTransactionCap, and the alerting rule reads TTLCounter.CapHitCount7d.MatchGreaterOrEqual(3), where the count is a placeholder. Exact equality fails if the cap is applied with fees or rounding; a ratio-in-range test is then the fallback. |
Matched = Payload.CurrencyAmount.MatchEqual(Dictionary.PerTransactionCap) |
Check: Per-transaction e-money caps vary by country. Verify the Dictionary value. Tag: Structuring · AML · Wallet · 7d |
| 9.7-10 | Wallet balance kept just under tier ceiling via sweeps | Balance management to avoid KYC. | TTL counter CeilingBand30d (Data Name WalletId, Interval Days / 30, count only) is incremented by the balance predicate, which tests the balance just below Dictionary TierCeiling, and the alerting rule reads TTLCounter.CeilingBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. Sweeps are caught by the repetition over time. |
Matched = Payload.Balance.MatchIsJustBelowThreshold(Dictionary.TierCeiling, 5) |
Check: Wallet tier ceilings are regulatory in some regimes. Verify the Dictionary value. Tag: Structuring · AML · Wallet · 30d |
| 9.7-12 | Corporate payments just under single-approver limit to same vendor | Approval-limit avoidance. | TTL counter UserVendorBand30d (Data Name UserVendorKey, a composite of approver and vendor the payload must carry, Interval Days / 30, count only) is incremented by the band predicate with Dictionary SingleApproverLimit, and the alerting rule reads TTLCounter.UserVendorBand30d.MatchGreaterOrEqual(3), where the count is a placeholder. A TTL counter takes one key field, which is why the composite is carried in the payload. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.SingleApproverLimit, 10) |
Tag: Structuring · Fraud · User+Vendor · 30d |
| 9.7-13 | Purchase orders split to avoid tender threshold | Procurement splitting. | TTL sum counter TenderBandSum90d (Data Name BuyerId, Interval Days / 90, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary TenderThreshold, and the alerting rule reads TTLCounter.TenderBandSum90d.MatchGreater(Dictionary.TenderThreshold), summing purchase orders per buyer against the tender level. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.TenderThreshold, 10) |
Check: Procurement tender thresholds are set by public procurement law (EU and UK levels differ). Verify the Dictionary value. Tag: Structuring · Fraud · Buyer · 90d |
| 9.7-14 | Expense items just under per-item receipt threshold | Receipt-limit clustering. | TTL counter ItemBand90d (Data Name EmployeeId, Interval Days / 90, count only) is incremented by the band predicate on the per-item amount with Dictionary ReceiptThreshold, and the alerting rule reads TTLCounter.ItemBand90d.MatchGreaterOrEqual(3), where the count is a placeholder. ItemAmount is a placeholder field name. |
Matched = Payload.ItemAmount.MatchIsJustBelowThreshold(Dictionary.ReceiptThreshold, 10) |
Check: Receipt thresholds are set by company policy and tax rules. Verify the Dictionary value. Tag: Structuring · Fraud · Employee · 90d |
| 9.7-15 | E-commerce orders split under fraud-screening value | Order splitting. | TTL counter ScreenBand24h (Data Name AccountId, Interval Hours / 24, count only) is incremented by the band predicate with Dictionary ScreeningValue, and the alerting rule reads TTLCounter.ScreenBand24h.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ScreeningValue, 10) |
Tag: Structuring · Fraud · Account · 24h |
| 9.7-16 | Gift-card orders at exactly the per-order cap repeated | Cap repetition. | TTL counter GiftCapHits7d (Data Name AccountId, Interval Days / 7, count only) is incremented by orders exactly equal to Dictionary PerOrderCap, and the alerting rule reads TTLCounter.GiftCapHits7d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.CurrencyAmount.MatchEqual(Dictionary.PerOrderCap) |
Tag: Structuring · Fraud · Account · 7d |
| 9.7-17 | BNPL purchases just under per-order limit across merchants | BNPL limit gaming. | Abstraction Rule BnplBandMerchants24h (Search Key CustomerId, Search Interval Hours / 24, Function Type Distinct Count on MerchantId) over orders in the band predicate with Dictionary BnplPerOrderLimit, read by Abstraction.BnplBandMerchants24h.MatchGreaterOrEqual(3), where the count is a placeholder. It counts merchants, not orders, so one merchant with many orders does not trip it. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.BnplPerOrderLimit, 10) |
Tag: Structuring · Fraud · Customer · 24h |
| 9.7-18 | Loan requests just under automated-approval ceiling | Auto-approve gaming. | TTL counter AutoApproveBand90d (Data Name ApplicantId, Interval Days / 90, count only) is incremented by the band predicate on the requested amount with Dictionary AutoApprovalCeiling, and the alerting rule reads TTLCounter.AutoApproveBand90d.MatchGreaterOrEqual(3), where the count is a placeholder. RequestedAmount is a placeholder field name. |
Matched = Payload.RequestedAmount.MatchIsJustBelowThreshold(Dictionary.AutoApprovalCeiling, 10) |
Tag: Structuring · Fraud · Applicant · 90d |
| 9.7-19 | Donations just under donor-disclosure level (NPO/political) | Disclosure avoidance. | TTL sum counter DonationBandSum12m (Data Name DonorId, Interval in months, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary DonorDisclosureLevel, and the alerting rule reads TTLCounter.DonationBandSum12m.MatchGreater(Dictionary.DonorDisclosureLevel), summing donations per donor over 12 months against the level. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.DonorDisclosureLevel, 10) |
Check: Donor disclosure levels differ between NPO and political regimes. Verify the Dictionary value. Tag: Structuring · AML · Donor · 12m |
| 9.7-20 | Trade shipments valued just under licence/inspection thresholds | Customs threshold avoidance. | TTL counter ShipmentBand90d (Data Name ExporterId, Interval Days / 90, count only) is incremented by the band predicate on the shipment value with Dictionary LicenceThreshold, and the alerting rule reads TTLCounter.ShipmentBand90d.MatchGreaterOrEqual(3), where the count is a placeholder. |
Matched = Payload.ShipmentValue.MatchIsJustBelowThreshold(Dictionary.LicenceThreshold, 10) |
Check: Licence and inspection thresholds are set by the export regime. Verify the Dictionary value. Tag: Structuring · AML · Exporter · 90d |
9.8 Double-threshold band construction patterns
| # | Example | Description | Jube construction | Fragment | Notes |
|---|---|---|---|---|---|
| 9.8-01 | Band 90–99.9% of limit | Amount within the top decile below the limit. | Single event, no counter: the ratio of the amount to the limit field Limit is tested for a band between 90 and 99.9 percent, so it follows a changing limit. The count and sum variants follow 9.8-02 and 9.8-04. |
Matched = Payload.CurrencyAmount.MatchRatioInRange(Payload.Limit, 0.9, 0.999) |
Tag: Structuring · Both · any · per event |
| 9.8-02 | Band with count condition | ≥ N events in band within window. | TTL counter BandCount (Data Name AccountId, an interval set by the specific rule, count only) is incremented by the band predicate with Dictionary Limit, and the alerting rule reads TTLCounter.BandCount.MatchGreaterOrEqual(N), where the window and N are supplied by the specific rule. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.Limit, 10) |
Tag: Structuring · Both · any · window |
| 9.8-03 | Band with distinct-location condition | Band events at ≥ M distinct branches/ATMs/agents. | Abstraction Rule BandBranches (Search Key CustomerId, a window set by the specific rule, Function Type Distinct Count on BranchId) over band events, read by Abstraction.BandBranches.MatchGreaterOrEqual(M), where M is supplied by the specific rule. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.Limit, 10) |
Tag: Structuring · AML · Customer · window |
| 9.8-04 | Band with aggregate condition | Sum of band events exceeds the limit itself. | TTL sum counter BandSum (Data Name AccountId, a window set by the specific rule, Sum on CurrencyAmount) is incremented by the band predicate with Dictionary Limit, and the alerting rule reads TTLCounter.BandSum.MatchGreater(Dictionary.Limit), so the aggregate of band events is compared with the limit itself. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.Limit, 10) |
Tag: Structuring · AML · Customer · window |
| 9.8-05 | Band with linked-entity condition | Band events across linked customers (shared address/device). | Abstraction Rule BandCustomersByDevice (Search Key DeviceId, a window set by the specific rule, Function Type Distinct Count on CustomerId) over band events, read by Abstraction.BandCustomersByDevice.MatchGreaterOrEqual(M), where M is supplied by the specific rule. This is one hop through a shared key in the payload; multi-hop clusters are graph computation, which routes to GraphDirectedWorkflows. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.Limit, 10) |
Tag: Structuring · AML · Cluster · window |
| 9.8-06 | Band with time-of-cutoff condition | Band events just before/after daily cut-off. | TTL counter BandCutoff3d (Data Name AccountId, Interval Days / 3, count only) is incremented by a rule that requires the amount to be just below Dictionary Limit and the transaction hour to be the hour before a midnight cut-off, and the alerting rule reads TTLCounter.BandCutoff3d.MatchGreaterOrEqual(N), where N is a placeholder. ToZone is needed first if the cut-off is in local time. |
Matched = Payload.CurrencyAmount.RequireIsJustBelowThreshold(Dictionary.Limit, 10).Against(Payload.TransactionTime).HourOfDay().MatchInRange(23, 23) |
Tag: Structuring · AML · Account · 3d |
| 9.8-07 | Band relative to dynamic limit | Limit is the customer’s own daily limit or tier, not a fixed regulatory one. | Single event, no counter: the amount is tested just below the customer’s own daily limit, carried in the payload as CustomerDailyLimit, so the threshold follows the customer. The equivalent ratio form is a ratio-in-range test against the same field. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Payload.CustomerDailyLimit, 10) |
Tag: Structuring · Fraud · Account · window |
| 9.8-08 | Band on exemption thresholds (SCA, contactless, PIN) | Payment limits used by authentication exemptions. | TTL counter ExemptionBand24h (Data Name CardId, Interval Hours / 24, count only) is incremented by the band predicate with Dictionary ExemptionThreshold, and the alerting rule reads TTLCounter.ExemptionBand24h.MatchGreaterOrEqual(N), where N is a placeholder. The exemption threshold is a Dictionary entry, so it can follow the scheme. |
Matched = Payload.CurrencyAmount.MatchIsJustBelowThreshold(Dictionary.ExemptionThreshold, 10) |
Check: SCA, contactless and PIN exemption values differ by scheme and jurisdiction and change over time. Verify the Dictionary value. Tag: Structuring · Fraud · Card/Account · window |
