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.

This guide walks you through the process of writing a Watch Script rule from scratch — from thinking about the fraud pattern to testing the finished rule against live transactions. By the end, you’ll have a fully functional .ws file and a clear understanding of the authoring workflow.

Thinking About Fraud Patterns

Before you write a single line of code, start by framing the fraud pattern you want to detect as a question:
“Should I be concerned if a transaction has [some characteristic]?”
This simple question forces you to articulate the behavior you’re targeting. Here are some examples:
  • “Should I be concerned if a single transaction exceeds $10,000?”
  • “Should I be concerned if the same account sends money to the same destination more than 10 times in 24 hours?”
  • “Should I be concerned if a large transaction happens at 3 AM from a normally dormant account?”
The answer to this question becomes your rule. The characteristic becomes your when clause. The level of concern becomes your score. And the action you’d take becomes your then verdict. For this tutorial, we’ll work with a concrete scenario:
Scenario: Your compliance team has flagged that transactions over $5,000 to foreign currencies should be reviewed. This is a common AML (Anti-Money Laundering) requirement.
Let’s turn this into a rule.

Creating a .ws File

Every rule lives in its own .ws (Watch Script) file. FinWatch monitors a directory for these files and automatically compiles them when they are created or modified.

Where to Create the File

Place your .ws files in the directory specified by the WATCH_SCRIPT_DIR environment variable. The default is watch_scripts/ relative to where FinWatch is running.
mkdir -p watch_scripts
touch watch_scripts/ForeignCurrencyHighValue.ws

Naming Conventions

  • File name: Use PascalCase matching the rule name (e.g., ForeignCurrencyHighValue.ws).
  • One rule per file. This is a strict convention. Each .ws file should contain exactly one rule. This ensures clean Git diffs, clear audit trails, and independent rule management.

Why One Rule Per File?

When a transaction is blocked or flagged, you need to know exactly which rule triggered it. If multiple rules live in one file, debugging and auditing become significantly harder. The one-rule-per-file convention also means each rule has its own version history in Git — you can see when it was created, who changed it, and why.

Step 1: Name Your Rule

Open ForeignCurrencyHighValue.ws in your editor and start with the rule declaration:
rule ForeignCurrencyHighValue {

}
The rule keyword declares a new rule. It is followed by the rule’s name — an identifier that must start with a letter or underscore and can contain letters, digits, and underscores. Naming tips:
  • Be descriptive: ForeignCurrencyHighValue is far better than Rule1 or CurrencyCheck.
  • Use PascalCase: It’s the convention across the FinWatch ecosystem.
  • Describe the pattern, not the action: HighFrequencyDestination is better than BlockFrequentTransactions because the verdict might change over time, but the pattern won’t.

Step 2: Describe Your Intent

Add a description inside the rule block:
rule ForeignCurrencyHighValue {
    description "Flags high-value transactions in non-USD currencies for AML review."
}
The description is a double-quoted string that explains what the rule does and why it exists. This is not just a comment — it’s a first-class part of the rule that is stored alongside it and surfaced in logs, API responses, and dashboards. Writing a good description: Think about who will read this. It won’t just be you. It will be:
  • A compliance officer who needs to understand why a transaction was flagged.
  • A fraud analyst reviewing a queue of flagged transactions at 2 AM.
  • A regulator auditing your fraud detection controls.
  • A future developer who inherits this codebase in two years.
Write for all of them. Be specific about the threshold, the pattern, and the regulatory context.
BadGood
"Checks currency""Flags high-value transactions in non-USD currencies for AML review."
"Amount rule""Reviews transactions over $5,000 in foreign currencies per BSA/AML compliance."

Step 3: Define the Conditions (when)

The when clause is where you define the logic. Let’s build it incrementally.

A Single Condition

Start with the simplest possible check — just the amount threshold:
rule ForeignCurrencyHighValue {
    description "Flags high-value transactions in non-USD currencies for AML review."

    when amount > 5000
}
This rule will fire on any transaction over $5,000, regardless of currency. Let’s make it more specific.

Adding a Second Condition with and

We only want to flag transactions that are both high-value and in a non-USD currency:
rule ForeignCurrencyHighValue {
    description "Flags high-value transactions in non-USD currencies for AML review."

    when amount > 5000
     and currency != "USD"
}
The and keyword means both conditions must be true for the rule to fire. If the amount is $6,000 but the currency is "USD", the rule does not fire. Formatting: Notice the alignment. The and keyword is indented to align with the start of the first condition. This is not required by the parser, but it dramatically improves readability. Adopt this convention early.