Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.finwatch.finance/llms.txt

Use this file to discover all available pages before exploring further.

A library of production-ready Watch Script rule patterns covering the most common fraud scenarios in fintech. Each recipe is a complete, copy-paste-ready .ws file with an explanation of why it works and how to customize it for your needs.

How to Use This Cookbook

  1. Find a recipe that matches the fraud pattern you want to detect.
  2. Copy the .ws file into your watch_scripts/ directory.
  3. Customize the thresholds — adjust amounts, time windows, and scores to match your risk appetite.
  4. Test with sample transactions — inject transactions that should and should not trigger the rule.
  5. Deploy — commit to your Git repository and let FinWatch pick it up.
Every recipe includes:
  • The complete rule — copy-paste ready.
  • Why this works — the fraud pattern this rule targets.
  • How to customize — which values to tune for your business.

Threshold Checks

High-Value Transaction Check

rule HighValueTransactionCheck {
    description "Monitors for unusually large transactions requiring review."

    when amount > 10000

    then review
         score   0.5
         reason  "Transaction amount exceeds 10,000 in any currency."
}
Why this works: The simplest and most important rule in any fraud detection system. Unusually large transactions are the most common vector for theft. This creates a baseline safety net that catches high-value transactions regardless of other factors. How to customize:
  • Threshold: Adjust 10000 based on your typical transaction sizes. A consumer payments platform might use 5000; a B2B platform might use 100000.
  • Score: At 0.5, this is moderate confidence. Increase to 0.7+ if high-value transactions are rare in your system.
  • Verdict: review is appropriate for most cases. Use block only if your business requires pre-authorization holds on large amounts.

Micro-Transaction Detection (Card Testing)

rule RapidSmallBurst {
    description "Multiple small transactions in quick succession."

    when amount < 500
     and count(when source == $current.source, "PT30M") >= 5

    then review
         score   0.65
         reason  "Rapid succession of small transactions"
}
Why this works: Card testing is a common fraud technique where stolen card numbers are validated with small purchases (often under $1-5) before being used for larger fraudulent transactions. This rule detects the pattern: many small transactions from the same source in a short window. How to customize:
  • Amount threshold: Lower to < 10 or even < 1 for stricter card testing detection.
  • Count threshold: >= 5 in 30 minutes is aggressive. Relax to >= 10 if your users legitimately make many small purchases (e.g., vending machines, transit systems).
  • Time window: "PT30M" catches rapid bursts. Extend to "PT1H" for slower-paced testing.

Cross-Border Transaction Flag

rule CrossBorderTransactionCheck {
    description "High-value transaction crossing international borders."

    when metadata.source_country != metadata.destination_country
     and amount > 1000

    then review
         score   0.5
         reason  "Large cross-border transaction requires review"
}
Why this works: Cross-border transactions carry inherent risk — different regulatory jurisdictions, currency exchange complexities, and difficulty in tracing funds. Many AML regulations require enhanced due diligence for international transfers. How to customize:
  • Amount: 1000 is a conservative threshold. Increase for high-volume cross-border businesses.
  • Metadata fields: Ensure your application sends source_country and destination_country in the meta_data object.
  • Score: Increase to 0.7+ if your business rarely processes international transactions.

Velocity and Volume

High-Frequency Transactions to Same Destination

rule HighFrequencyDestination {
    description "Unusually frequent payments to the same destination may require scrutiny."

    when count(when destination == $current.destination, "PT24H") > 10
     and amount > 100

    then review
         score   0.5
         reason  "High frequency of transactions to same destination in 24 hours"
}
Why this works: A single destination receiving many transactions is a hallmark of money mule accounts — intermediary accounts used to collect and redistribute stolen funds. Legitimate businesses rarely receive more than a handful of transactions from distinct sources in a day. How to customize:
  • Count: > 10 is moderate. Lower to > 5 for stricter monitoring; raise to > 20 for high-volume merchant accounts.
  • Amount gate: The amount > 100 condition filters out micro-transactions, reducing false positives from legitimate high-frequency, low-value payments.

Source Account High Outflow

rule SourceHighOutflow {
    description "Source account has high outflow volume in 24h."

    when sum(amount when source == $current.source, "PT24H") > 5000

    then review
         score   0.5
         reason  "High cumulative outflow from source in 24 hours"
}
Why this works: Account draining — where a compromised account is quickly emptied through many transfers — is one of the most damaging fraud patterns. This rule detects it by monitoring total outflow, not individual transaction amounts. How to customize:
  • Sum threshold: 5000 is conservative. Set based on your typical user’s daily spending. A fintech serving gig workers might use 2000; a corporate treasury platform might use 500000.
  • Time window: "PT24H" is standard for daily limits. Use "PT1H" for near-real-time draining detection.

High-Value Velocity from Single Source

rule HighAmountVelocitySource {
    description "Monitors for rapid high-value spending from a single source in the last hour."

    when sum(amount when source == $current.source, "PT1H") > 3000

    then review
         score   0.7
         reason  "Source account shows high-velocity spending pattern"
}
Why this works: A tighter version of the high outflow rule with a 1-hour window and higher score. Detects rapid account draining in near-real-time rather than over a full day.

Repeated Identical Amounts

rule RepeatedIdenticalAmount {
    description "Multiple transactions with identical amounts may indicate structuring."

    when count(when amount == $current.amount, "PT1H") > 2

    then review
         score   0.6
         reason  "Multiple identical amount transactions in a short time period"
}
Why this works: Structuring (smurfing) often involves multiple transactions of the same amount to stay under reporting thresholds. Legitimate users rarely make multiple transactions for exactly the same amount in a short period. How to customize:
  • Count: > 2 means 3+ identical amounts in an hour triggers the rule. Adjust based on your business — some retail scenarios legitimately have repeated amounts.
  • Time window: "PT1H" is tight. Extend to "PT24H" to catch slower structuring patterns.

Behavioral Anomalies

Dormant Account Reactivation

rule DormantAccountActivity {
    description "Unusual activity after long period of account dormancy."

    when metadata.days_since_last_transaction > 90
     and amount > 1000

    then review
         score   0.7
         reason  "High-value transaction after extended account inactivity"
}
Why this works: When an account that’s been dormant for 90+ days suddenly makes a large transaction, it’s a strong signal of account takeover. The legitimate owner likely isn’t using the account; someone else gained access. How to customize:
  • Dormancy period: 90 days is standard. Lower to 30 for more aggressive detection.
  • Amount: 1000 filters out small reactivation transactions (e.g., checking if the account still works).
  • Metadata requirement: Your application must include days_since_last_transaction in the transaction’s meta_data.

New Account First-Day Activity

rule NewAccountFirstDay {
    description "High-volume activity on first day of account creation."

    when metadata.account_age_days < 1
     and amount > 1000

    then review
         score   0.6
         reason  "Large transaction from newly created account"
}
Why this works: Fraudulently created accounts are typically used within the first few hours. A legitimate user might fund their account with a small test deposit; a fraudster goes straight for a large transaction. How to customize:
  • Account age: < 1 day catches same-day activity. Extend to < 7 for the critical first-week period.
  • Amount: Lower to 500 for consumer platforms; raise for B2B.
  • Combine with velocity: Add and count(when source == $current.source, "PT1H") > 3 to catch rapid activity from new accounts.

Unusual Transaction Time

rule UnusualTransactionTime {
    description "Large transactions during unusual hours receive extra scrutiny."

    when hour_of_day(timestamp) >= 1
     and hour_of_day(timestamp) < 5
     and amount > 1000

    then review
         score   0.6
         reason  "Large transaction during unusual hours (1 AM - 5 AM)"
}
Why this works: Most legitimate financial activity happens during waking hours. Transactions between 1 AM and 5 AM (UTC) are statistically more likely to be fraudulent — especially large ones. How to customize:
  • Time range: Adjust for your users’ time zones. See the Time-Based Rules Guide for UTC offset considerations.
  • Amount: Lower the threshold for higher sensitivity.

Late Night Transactions

rule LateNightTransactions {
    description "Detects transactions made late at night."

    when hour_of_day(timestamp) >= 23
      or hour_of_day(timestamp) <= 3

    then review
         score   0.4
         reason  "Transaction occurred during late night hours"
}
Why this works: A broader version of the unusual time rule. Catches any transaction between 11 PM and 3 AM, regardless of amount. Lower score because late-night activity alone isn’t highly suspicious.

Weekend Transaction on Business Account

rule WeekendTransactionCheck {
    description "Flags high-value transactions on weekends for business accounts."

    when day_of_week(timestamp) == 0
      or day_of_week(timestamp) == 6

    then review
         score   0.4
         reason  "Transaction on a weekend"
}
Why this works: Business accounts typically don’t process payments on weekends. Weekend activity from a corporate account could indicate unauthorized access. How to customize:
  • Add and metadata.account_type == "business" to target only business accounts.
  • Add and amount > 5000 to focus on significant transactions.

Identity and KYC

Low KYC Daily Limits

rule LowKycDailyTotal {
    description "Low-tier KYC total spend in 24h above threshold."

    when metadata.kyc_tier == 1
     and sum(amount when source == $current.source, "PT24H") > 5000

    then review
         score   0.5
         reason  "Total transacted amount exceeds tier-1 limit"
}
Why this works: KYC tiering is standard in fintech. Users with basic verification (tier 1) should have lower daily limits. This rule enforces that limit by monitoring cumulative daily spending. How to customize:
  • KYC tier: Adjust for your tier system. Create separate rules for each tier with different thresholds.
  • Daily limit: 5000 is illustrative. Set based on your regulatory requirements.

Low KYC High-Risk Activity

rule LowKycHighRisk {
    description "Low-tier KYC customer engaging in high-risk activity."

    when metadata.kyc_tier == 1
     and metadata.merchant_category in ("gambling", "cryptocurrency", "adult", "high_value_goods")
     and amount > 500

    then review
         score   0.8
         reason  "Low-KYC account engaging in high-risk category transaction"
}
Why this works: Low-KYC users transacting in high-risk categories (gambling, crypto, etc.) represent elevated risk. The combination of weak identity verification and risky transaction categories is a common fraud vector. How to customize:
  • Categories: Adjust the list based on your risk assessment. Replace with MCC codes if you have them.
  • Score: 0.8 is high. This is appropriate because the combination of low KYC and high-risk category is a strong signal.

Self-Transfer Detection

rule SelfTransferCheck {
    description "Detects when the source and destination of a transaction are the same."

    when source == $current.destination

    then review
         score   0.5
         reason  "Self-transfer detected — source and destination are identical"
}
Why this works: Sending money to yourself is a common technique in money laundering — the funds appear to have moved through the system, but they’re actually staying with the same entity. It can also indicate a configuration error.

Sanctions and Compliance

Sanctioned Country Check

rule SanctionedCountryCheck {
    description "Blocks transactions to sanctioned countries per OFAC/international sanctions."

    when metadata.destination_country in $sanctioned_countries

    then block
         score   1.0
         reason  "Destination country is on global sanctions list"
}
Why this works: This is a mandatory rule for any regulated financial institution. Transactions to sanctioned countries must be blocked outright. Using a variable ($sanctioned_countries) means the list can be updated without modifying the rule. How to customize:
  • Variable: Maintain $sanctioned_countries externally. Update it whenever OFAC, EU, or UN sanctions lists change.
  • Verdict: block is non-negotiable for sanctions compliance.
  • Score: 1.0 — maximum confidence.

High-Risk Destination Country

rule HighRiskDestinationCountry {
    description "Destination country on high-risk list."

    when metadata.destination_country in $high_risk_countries

    then block
         score   0.5
         reason  "Destination country classified as high risk"
}
Why this works: Distinct from sanctioned countries, high-risk countries may not require an outright block but warrant enhanced due diligence. Common high-risk lists include FATF grey/black lists. How to customize:
  • Consider using review instead of block for high-risk (non-sanctioned) countries.
  • The $high_risk_countries variable can include FATF-listed jurisdictions.

Suspicious Description Patterns

rule SuspiciousDescriptionCheck {
    description "Detects suspicious keywords or patterns in transaction descriptions."

    when description regex "(?i)(btc|bitcoin|crypto|wallet|transfer|gift.?card|western.?union)"
     and amount > 1000

    then review
         score   0.2
         reason  "Suspicious description pattern."
}
Why this works: Transaction descriptions containing cryptocurrency keywords, gift card references, or money transfer service names are common in scam-related payments (romance scams, tech support scams, etc.). How to customize:
  • Pattern: Extend with additional keywords relevant to your market. Be careful not to make the pattern too broad — you don’t want to flag legitimate crypto exchanges.
  • Amount gate: 1000 filters noise. Lower for higher sensitivity.
  • Score: 0.2 is low because description matching alone has high false-positive rates. Layer with other conditions for higher confidence.

Known Fraud Entity Check

rule KnownFraudEntityCheck {
    description "Automatically blocks transactions with entities on fraud list."

    when destination in $known_fraud_entities

    then block
         score   1.0
         reason  "Destination is on known fraud entities list"
}
Why this works: If you maintain a list of known fraudulent accounts (from previous investigations, shared intelligence, or fraud databases), this rule provides an automatic hard block. How to customize:
  • Use a variable ($known_fraud_entities) rather than an inline list. This list changes frequently as new fraud accounts are identified.
  • Consider also checking source in $known_fraud_entities for inbound fraud detection.

Cryptocurrency Category Check

rule CategoryBTCCheck {
    description "Cryptocurrency transaction requires manual verification."

    when metadata.category == "cryptocurrency"

    then review
         score   0.6
         reason  "Cryptocurrency transaction requires verification"
}
Why this works: Cryptocurrency transactions carry elevated risk due to the irreversible nature of blockchain transfers and the pseudonymous nature of crypto wallets. Many compliance frameworks require additional verification.

Foreign Currency Transaction

rule ForeignCurrencyTx {
    description "Transaction occurs in currency different from account's base currency."

    when currency != metadata.account_base_currency
     and amount > 1000

    then review
         score   0.4
         reason  "Large transaction in foreign currency"
}
Why this works: When an account normally transacts in one currency but suddenly uses a different one, it could indicate the account is being used from a different country — potentially by someone other than the legitimate owner.

Sequential Patterns

Block After Previous Failure

rule BlockIfPreviousFailed {
    description "Block when previous transaction failed for same source."

    when previous_transaction(
        within: "PT1H",
        match: {
            status: "failed",
            source: "$current.source"
        }
    )
    and amount > 700000

    then block
         score   1.0
         reason  "High-value transaction after recent failure from same source"
}
Why this works: A failed transaction followed by a large retry is a classic account takeover signal. The initial failure might have been due to the fraudster hitting an incorrect limit, after which they adjust and try again with a larger amount. How to customize:
  • Time window: "PT1H" is tight. Extend to "PT2H" or "PT4H" for broader coverage.
  • Amount: 700000 is very high. Lower based on your typical transaction sizes.
  • Failed status: Ensure your system records failed transactions with status: "failed".

Merchant-Issuer Country Mismatch

rule MerchantIssuerMismatch {
    description "Card issuer country does not match merchant country."

    when metadata.merchant_country != metadata.card_issuer_country

    then review
         score   0.4
         reason  "Card issuer country does not match merchant country"
}
Why this works: When a card issued in one country is used at a merchant in a different country, it’s a geographical anomaly that could indicate the card data was stolen and used remotely. How to customize:
  • Add and amount > 500 to filter out small international purchases (common for online shopping).
  • Combine with and metadata.is_card_present == false to focus on card-not-present (CNP) fraud.

Combining Recipes

The most effective fraud detection comes from layering multiple rules. Here’s a recommended starter set for a typical fintech platform:
PriorityRuleVerdictWhy
1SanctionedCountryCheckblockRegulatory requirement
2KnownFraudEntityCheckblockKnown bad actors
3BlockIfPreviousFailedblockAccount takeover signal
4HighValueTransactionCheckreviewHigh-value safety net
5SourceHighOutflowreviewAccount draining
6RapidSmallBurstreviewCard testing
7DormantAccountActivityreviewAccount takeover
8LowKycDailyTotalreviewKYC compliance
9SuspiciousDescriptionCheckreviewScam detection
10LateNightTransactionsalertMonitoring baseline
Start with this set, monitor the verdicts for a week, tune the thresholds based on your false-positive and false-negative rates, and then add more specialized rules.

Next Steps