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.

Getting Started with FinWatch

This guide takes you from zero to a running FinWatch instance with your first fraud detection rule evaluated — in under 10 minutes.

What is FinWatch?

FinWatch is an embeddable, developer-first fraud detection engine designed to run alongside your fintech application. Unlike traditional fraud detection platforms that operate as opaque, external services, FinWatch deploys directly into your infrastructure and gives you full transparency and control over the rules that govern your transaction monitoring. At its core, FinWatch provides three things:
  1. The Watch Script DSL — A purpose-built domain-specific language for writing fraud detection rules. Rules are stored as .ws files that can be versioned in Git, reviewed in pull requests, and deployed with the same rigour as your application code. The language is designed to express complex fraud patterns — velocity checks, time-based anomalies, sequential behaviour analysis — in a readable, declarative format.
  2. An Embedded Analytical Engine — FinWatch uses DuckDB, a high-performance embedded analytical database, to store and query transaction data locally. This means aggregate functions like “count the number of transactions from this account in the last 24 hours” execute in milliseconds without requiring a round-trip to an external database. DuckDB’s columnar storage and vectorised execution engine are optimised for exactly this type of analytical workload.
  3. GitOps-Native Rule Management — Rules can be synced from a Git repository, enabling a full CI/CD lifecycle for fraud logic; write a rule, open a PR, get it reviewed by your compliance team, merge, and FinWatch picks it up automatically. Every change is audited, every version is recoverable.
The result is a fraud detection system that is fast to deploy, transparent to audit, and powerful enough to catch sophisticated fraud patterns that simple regex-based systems miss entirely.

Prerequisites

Before you begin, make sure you have the following:
  • Docker (recommended) — Install Docker
  • curl — For sending HTTP requests from the command line. Pre-installed on macOS and most Linux distributions.
  • A text editor — For writing .ws rule files. Any editor works; VS Code is recommended.
  • Basic terminal knowledge — You should be comfortable running commands in a terminal.
If you plan to build from source instead of using Docker:
  • Go 1.21+Install Go
  • Git — For cloning the repository and (optionally) for GitOps rule management.

Installation

Docker

The fastest way to get FinWatch running:
docker run -d \
  --name finwatch \
  -p 8081:8081 \
  -e WATCH_SCRIPT_DIR=/app/watch_scripts \
  -v $(pwd)/my_rules:/app/watch_scripts \
  finwatch/finwatch:latest
This command:
  • Starts FinWatch in the background (-d).
  • Maps port 8081 on your host to port 8081 in the container.
  • Sets the watch script directory to /app/watch_scripts inside the container.
  • Mounts a local directory (my_rules/) so you can add and edit rule files from your host machine.
Verify it’s running:
curl http://localhost:8081/instructions
You should see an empty JSON array [] — this means FinWatch is running and ready.

Environment Variables

FinWatch’s behaviour is configured through environment variables. Here is a complete reference:

WATCH_SCRIPT_DIR

Default: watch_scriptsDirectory where .ws rule files are stored and watched for changes.

WATCH_SCRIPT_GIT_REPO

Default: (empty)Git repository URL to sync rules from. If set, enables GitOps mode.

WATCH_SCRIPT_GIT_BRANCH

Default: mainThe Git branch to track for rule updates.

CLIENT_DSN

Default: (empty)PostgreSQL connection string for the database. Enables the watermark sync feature.

FINWATCH_MEMORY_LIMIT

Default: 2GiBMaximum memory DuckDB is allowed to use. Accepts values like 1GiB, 4GiB, 512MiB.

FINWATCH_PORT

Default: 8081The HTTP port FinWatch listens on.

Your First Transaction

With FinWatch running, let’s inject a transaction. This simulates what your application would do in production: sending a transaction to FinWatch for real-time risk evaluation. Run the following curl command:
curl -X POST http://localhost:8081/inject \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "txn_001",
    "amount": 15000.00,
    "currency": "USD",
    "source": "acct_alice_123",
    "destination": "acct_bob_456",
    "reference": "invoice_2026_001",
    "description": "Consulting payment",
    "status": "pending",
    "created_at": "2026-04-18T14:30:00Z",
    "meta_data": {
      "ip_address": "192.168.1.100",
      "destination_country": "US",
      "device_type": "mobile"
    }
  }'
Field-by-field explanation:
FieldTypeRequiredDescription
transaction_idstringNoA unique identifier. Auto-generated as a UUID if not provided.
amountfloatYesThe monetary value of the transaction.
currencystringYesThe ISO 4217 currency code (e.g., "USD", "EUR", "NGN").
sourcestringNoThe account or entity initiating the transaction.
destinationstringNoThe account or entity receiving the transaction.
referencestringYesA unique reference string for idempotency.
descriptionstringNoA human-readable description of the transaction.
statusstringNoThe current status of the transaction (e.g., "pending", "applied", "failed").
created_atstring (RFC3339)NoThe timestamp of the transaction. Defaults to the current time if not provided.
meta_dataobjectNoArbitrary key-value metadata. This is where you put custom fields your rules can reference (e.g., IP address, country, device type, MCC codes).
A successful response returns HTTP 200 OK with an empty body. The transaction is now stored in DuckDB and has been evaluated against all active rules. At this point, you have no rules — so nothing was flagged. Let’s fix that.

Your First Rule

Create a directory for your rules (if you haven’t already) and add a new file:
mkdir -p my_rules
Now create a file called HighValueCheck.ws inside my_rules/:
rule HighValueCheck {
    description "Flags any transaction over $10,000 for manual review."

    when amount > 10000

    then review
         score  0.7
         reason "Transaction amount exceeds $10,000 high-value threshold"
}
What this rule does:
  • Name: HighValueCheck — A clear, descriptive identifier.
  • Description: Explains the rule’s purpose in plain English for auditors and analysts.
  • Condition (when): Checks if the transaction’s amount field is greater than 10000.
  • Action (then):
    • Verdict: review — Flag for human analyst review (don’t hard-block).
    • Score: 0.7 — High confidence that this warrants attention.
    • Reason: A clear explanation that will appear in logs and dashboards.
FinWatch watches the my_rules/ directory for changes. Within seconds of saving the file, you should see a log message indicating the rule was compiled:
INF Compiled watch script: HighValueCheck

Testing the Rule

Now inject the same transaction again:
curl -X POST http://localhost:8081/inject \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "txn_002",
    "amount": 15000.00,
    "currency": "USD",
    "source": "acct_alice_123",
    "destination": "acct_bob_456",
    "reference": "invoice_2026_002",
    "description": "Consulting payment",
    "status": "pending",
    "meta_data": {
      "destination_country": "US"
    }
  }'
This time, the $15,000 transaction exceeds the $10,000 threshold in your rule. In the FinWatch logs, you’ll see that the HighValueCheck rule fired and produced a review verdict with a 0.7 risk score. Now inject a transaction that should not trigger the rule:
curl -X POST http://localhost:8081/inject \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "txn_003",
    "amount": 500.00,
    "currency": "USD",
    "source": "acct_charlie_789",
    "destination": "acct_dave_012",
    "reference": "coffee_shop_001",
    "description": "Coffee purchase"
  }'
This $500 transaction falls well below the $10,000 threshold. The rule does not fire. No verdict is produced.

What Just Happened?

Here’s the complete flow of what happened when you injected that $15,000 transaction:
  1. Ingestion: The HTTP handler receives the JSON payload and decodes it into a Transaction struct.
  2. Storage: The transaction is inserted into the local DuckDB transactions table. This makes it available for aggregate queries by other rules.
  3. Rule Evaluation: The engine loads all compiled rules and evaluates them against the transaction. Each rule’s when clause is checked. If it evaluates to true, the then clause produces a RiskVerdict.
  4. Risk Consolidation: All verdicts are aggregated. The final risk score, verdict, and reason are determined.
  5. Anomaly Reporting: If the final verdict warrants it, an anomaly notification is sent to the FinWatch Cloud dashboard via the WebSocket tunnel for real-time alerting.

Next Steps

You’ve successfully installed FinWatch, injected a transaction, and written your first rule. Here’s where to go from here:
  • Writing Your First Rule — A deeper tutorial on building rules step by step.
  • Conditions Deep Dive — Learn every operator, logical combinator, and pattern matching technique available in the when clause.
  • Aggregate Functions Guide — The most powerful feature of the DSL: detecting fraud patterns over time using count(), sum(), avg(), and more.
  • The Rule Cookbook — A library of production-ready rule patterns you can copy and customise.
  • DSL Reference — The complete language reference for the Watch Script DSL.
  • API Documentation — Full REST API reference for all FinWatch endpoints.