What Are Velocity Checks?
Velocity checks are fraud and risk rules that measure how often, or how much, something happens per key within a time window, and act when a threshold is crossed. The key is an identifier such as a card, device, IP address, email or account; the measure is a count, a sum of amounts or a count of distinct values; and the action is to decline, send to review or ask for step-up authentication. For example, a rule might decline a card after five authorization attempts in ten minutes. A velocity check is only as accurate as the count it reads at the moment the transaction is evaluated.
Updated
What are velocity checks?
Velocity rules are a basic fraud defense because many attacks are fast and repetitive. Card testing runs many small authorizations to find which stolen card numbers work. Account takeover often shows up as a burst of logins or password resets. Money mules move funds through an account faster than its history predicts.
Payment platforms expose velocity as rule attributes. Stripe's Radar rules reference calls attributes like total_charges_per_email_daily "velocity rules" and notes: "A common use case for these attributes is to reduce card testing or enumeration attack scenarios." Its supported attributes include authorized_charges_per_card_number_hourly and authorized_charges_per_ip_address_daily. The rules reference also documents two details worth copying: counts over time windows exclude "the payment that you're currently processing," and windows are built from buckets, so "an hourly attribute interval might be 3900 seconds."
How velocity checks work
Each rule is a key, a window, a measure and a threshold. Computed on demand, it is a range query over recent events:
-- Attempts and amount on this card in the past 10 minutes, at decision time.
-- Needs an index on (card_id, attempted_at).
SELECT count(*) AS attempts_10m,
coalesce(sum(amount), 0) AS amount_10m
FROM auth_attempts
WHERE card_id = $1
AND attempted_at > now() - interval '10 minutes';At higher volume, teams often precompute instead: a counter in a cache, or a stream job that writes windowed counts to a key-value store. Each read gets cheap, but a count updated by an asynchronous pipeline lags the events it should include.
A worked failure, with illustrative numbers. The rule declines at five prior attempts in ten minutes. The count comes from a stream pipeline that lands each attempt four seconds after it happens, and an attempt counts from the instant it lands. A card-testing script sends three attempts per second.
| Attempt | Time (s) | Attempts already made | Count the rule reads | Verdict |
|---|---|---|---|---|
| 1 | 0.00 | 0 | 0 | Approve |
| 6 | 1.67 | 5 | 0 | Approve; a current count declines here |
| 13 | 4.00 | 12 | 1 | Approve |
| 16 | 5.00 | 15 | 4 | Approve |
| 17 | 5.33 | 16 | 5 | Decline |
The rule meant to stop the card after five attempts approves sixteen. Concurrency adds a second problem: two attempts evaluated at the same moment read the same count, even when it is current. In PostgreSQL's default Read Committed level, a query "never sees either uncommitted data or changes committed by concurrent transactions during the query's execution," per the PostgreSQL docs. A hard per-card limit needs those checks serialized, for example by locking a per-card row with SELECT ... FOR UPDATE before counting, which makes a concurrent check on the same card wait until the first transaction ends.
Why velocity checks matter in real-time systems
A velocity check is derived context: a count computed from transactions. When a pipeline prepares it, it lags the transactions that should update it, and a burst attack fits inside that lag. Bursts are when the rule matters most.
The fix is to compute counts that must reflect the latest transaction on demand, against committed data, and precompute only long windows. In Tacnode, short-window counts can run on demand against the attempts and transactions ingested from the systems you run, in the same snapshot as the account, device and risk data the rule reads. Longer windows can be incremental views, refreshed incrementally on a schedule you set in SQL, typically every few seconds. Tacnode gives the check accurate context; enforcement stays in your authorization service. Where the attempt records or holds are written to Tacnode itself, the writes are transactional, so concurrent checks against the same limit are serialized. More on where fraud architectures break is in real-time fraud detection architecture.
FAQ
Related terms
State velocity is the rate at which the state a decision depends on changes. High velocity plus concurrency is what makes cached context structurally invalid.
Derived context is state computed from events rather than stored directly — aggregates, velocity counts, features, embeddings — that decisions consume.
A race condition occurs when a system's outcome depends on the timing of concurrent operations, letting interleaved reads and writes corrupt shared state.
A validity window is the interval within which a decision's context remains an accurate basis for action — typically 10ms to 1s for automated decisions.
Point-in-time correctness means each training example gets feature values as they stood at the label's timestamp, never later ones, so no future data leaks.
Context lag is the delay between a fact changing at its source and the system an automated decision reads from reflecting it. Also called stale context.
A real-time feature store serves ML features that reflect events from seconds ago, fast enough for online prediction, using streaming and request-time features.
