Some fraud patterns can’t be detected by looking at a single transaction or even aggregating numbers over time. They require understanding the sequence of events — what happened before this transaction, and does the combination tell a story of fraud? TheDocumentation Index
Fetch the complete documentation index at: https://docs.finwatch.finance/llms.txt
Use this file to discover all available pages before exploring further.
previous_transaction() function gives you this power.
What is Sequential Pattern Detection?
Sequential pattern detection is the ability to ask: “Has something specific happened recently that makes this current transaction suspicious?” Consider these real-world fraud patterns:- Retry after failure: A transaction fails (insufficient funds, declined card), and within minutes, a new transaction comes in from the same source with a slightly different amount. This is a common pattern in account takeover — the fraudster is probing limits.
- Credential stuffing follow-up: A login attempt from a new device is followed by a large transfer. The device change indicates possible account compromise.
- Rapid reversal: A refund or reversal is immediately followed by a new purchase for a larger amount. This is common in refund fraud.
The previous_transaction() Function
Syntax
Parameters
| Parameter | Type | Description |
|---|---|---|
within | Named argument, string | ISO 8601 duration defining how far back to look (e.g., "PT1H" for 1 hour). |
match | Named argument, object | A set of key-value pairs that previous transactions must satisfy. All pairs must match (AND logic). |
Return Value
Boolean —true if at least one historical transaction exists within the time window that matches ALL of the specified criteria. false otherwise.
How match Works
The match object defines the criteria for finding a previous transaction. Each key is a field name in the transactions table, and each value is either:
- A literal value:
"failed","blocked",100— matches transactions where that field equals this exact value. - A
$currentreference:"$current.source"— resolved at runtime to the current transaction’s field value. This lets you find previous transactions from the same user, account, or device.
match object to count.
Example: Block After Previous Failure
This is the most common use case forprevious_transaction(). If a recent transaction from the same source failed, and now a new high-value transaction is coming in, it could indicate a fraudster probing account limits.
Breaking It Down
within: "PT1H"— Look back 1 hour from now.match: { status: "failed" }— Find transactions where thestatusfield equals"failed".match: { source: "$current.source" }— And where thesourcefield matches the current transaction’s source. If the current transaction hassource: "acct_alice", this resolves tosource: "acct_alice".and amount > 700000— Additionally, the current transaction must be over 700,000 (a high-value threshold).then block score 1.0— If both conditions are true, hard-block the transaction with maximum confidence.
What the Engine Does Internally
The interpreter builds a SQL query:true.
Testing This Rule
Step 1: Inject a failed transaction:acct_alice within the last hour, and the current transaction exceeds 700,000. Verdict: block, score: 1.0.
Step 3: Inject a high-value transaction from a different source:
acct_dave in the last hour.
Example: Detecting Account Takeover Patterns
Account takeover (ATO) often follows a predictable sequence: a suspicious event (device change, password reset) followed by a large transfer. You can model this withprevious_transaction() if your system logs these events as transactions or includes relevant metadata.
Note: This requires your system to log password change events as transactions (or as records in the transactions table). If your system stores these events differently, you’ll need to adapt the approach.
Example: Detecting Rapid Retry Patterns
Fraudsters often retry transactions with slightly modified parameters after an initial failure — adjusting the amount, changing the destination, or switching currencies.Combining previous_transaction() with Aggregates
The most sophisticated rules combine previous_transaction() with aggregate functions to create layered detection. This lets you check both the sequence of events and the volume of activity.
- A failed transaction from this source in the last hour (sequential pattern).
- More than 3 transactions from this source in the last hour (velocity).
- The current transaction is over $1,000 (significance threshold).
Combining with Time Functions
Performance Notes
previous_transaction() is the most expensive operation in the FinWatch DSL because it executes a SQL query against DuckDB for each evaluation.
How the Query Works
For eachprevious_transaction() call, the interpreter:
- Resolves all
$current.*placeholders in thematchobject. - Builds a
WHEREclause with one condition per match key-value pair. - Adds a timestamp filter for the
withinwindow. - Executes
SELECT COUNT(*) FROM transactions WHERE ... LIMIT 1. - Returns
trueif count > 0.
Optimization Tips
- Use the smallest
withinwindow possible."PT15M"scans far less data than"P1D". - Gate with cheap conditions. Always put simple checks (
amount > X) beforeprevious_transaction()in yourandchain. This avoids running expensive SQL queries on low-value transactions: - Be specific in your
matchcriteria. The more fields you specify inmatch, the more selective the query is, and the faster it returns. - Avoid very large time windows. A
within: "P30D"window on a high-traffic system can be slow. If you need long lookback periods, consider using aggregate functions instead (count()with a large window), as those are optimized with the batch aggregate context.
Next Steps
- Variables and Dynamic Data — Learn how
$currentand$variableswork across all function types. - Aggregate Functions Guide — Compare
previous_transaction()with aggregate approaches. - The Rule Cookbook — See production-ready rules that use sequential pattern detection.
- Conditions Deep Dive — Master the full condition system.
- DSL Reference — Complete function reference.
.png?fit=max&auto=format&n=0JF6z69u57hmqsWm&q=85&s=531373acedba0eb783b669f6d558dfd8)