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 functionalDocumentation Index
Fetch the complete documentation index at: https://docs.finwatch.finance/llms.txt
Use this file to discover all available pages before exploring further.
.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]?”
- “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?”
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:
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.
Naming Conventions
- File name: Use
PascalCasematching the rule name (e.g.,ForeignCurrencyHighValue.ws). - One rule per file. This is a strict convention. Each
.wsfile 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
OpenForeignCurrencyHighValue.ws in your editor and start with the rule declaration:
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:
ForeignCurrencyHighValueis far better thanRule1orCurrencyCheck. - Use
PascalCase: It’s the convention across the FinWatch ecosystem. - Describe the pattern, not the action:
HighFrequencyDestinationis better thanBlockFrequentTransactionsbecause the verdict might change over time, but the pattern won’t.
Step 2: Describe Your Intent
Add adescription inside the rule block:
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.
| Bad | Good |
|---|---|
"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:Adding a Second Condition with and
We only want to flag transactions that are both high-value and in a non-USD currency:
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..png?fit=max&auto=format&n=0JF6z69u57hmqsWm&q=85&s=531373acedba0eb783b669f6d558dfd8)