Velocity catches the customer sending too fast. It cannot catch a farm that never sends fast from any single key. Network reputation is the layer that answers the second question — is this whole network a farm? — and it exists because rotating identifiers defeat counters by design.
Why rotating keys defeat velocity
Velocity keys on account, phone, IP, and network at once, in a single Redis eval. That stops the obvious flood. But an attacker who rotates accounts, cycles phone numbers, and spreads across a residential proxy pool keeps every individual counter under its limit. Each key looks calm. The aggregate is an attack.
Counters can't see the aggregate. That's not a tuning problem — it's structural. A rolling window over one key has no memory of the thousand other keys doing the same thing next door.
Aggregating the network
So reputation zooms out. It aggregates traffic to the /24 for IPv4 and the /48 for IPv6 — a deliberately wider bucket than the velocity key, which stays narrow (full v4 address, or the /64 for v6) to identify one customer.
Different question, different truncation. Velocity wants the narrowest thing that identifies a sender. Reputation wants the block a farm actually operates behind, so rotating the low bits inside it changes nothing.
The signal: confirmation rate
A network is judged on its confirmation rate — the share of sends that ended with a code actually entered, reported via POST /v1/confirm. Real users type codes in. Fraud farms never do; typing the code earns them nothing.
Once a network passes a minimum sample size, if its confirmation rate sits at or below the threshold, the network is blocked. The minimum sample matters: two sends and zero confirmations is noise, not a verdict. You wait until the silence is statistically real before acting on it.
Read before velocity is consumed
Reputation is checked before velocity counters are recorded — on purpose.
Picture a carrier NAT: thousands of real subscribers and one farm share the same public /24. If a request from that network is going to be denied on reputation, it must not first burn a velocity slot. Consuming quota on a doomed request would let the farm drain the shared allowance and start denying the legitimate users sitting behind the same NAT.
So the order is: judge the network, then — only if it passes — spend the counter. A rejected network costs its neighbors nothing.
That ordering is the whole point. Velocity protects one customer from itself. Reputation protects everyone else from a network that learned to stay under every individual limit.