Fidro Now Remembers: Velocity Counts and Anomaly Detection on Every Validate Call
October 2, 2026
Until today every call to Fidro was a question with no memory. You sent an email and an IP, Fidro scored them against disposable domains, VPN and proxy lists, Tor exit nodes and your blocklists, and forgot the call had ever happened. That is fine for the first signup from an address. It is useless for the fifth.
From today, POST /api/validate accepts two optional fields, event and user_id. Send them and Fidro records the call, counts related activity over the last hour, day and month, and adds the anomalies it finds to the score and the response. It is on every plan, including Free, and the Track signups and logins guide has the three-line change.
The problem with stateless checks
Most signup abuse does not arrive from a Tor exit node with a mailinator address. It arrives from an ordinary residential IP with a plausible Gmail address, then another, then another. Each one is clean on its own. The pattern is only visible across calls, and until now the only place that pattern could be seen was your own database, after the accounts already existed.
The same applies to logins. A user who has signed in from the UK three hundred times and suddenly appears from a datacenter in another country is not a bad IP problem, because the IP may be fine. It is a "this is not how this person behaves" problem, and answering it needs history.
Fidro now keeps that history for you.
What you send
Two fields, both optional:
{
"email": "alice@example.com",
"ip": "203.0.113.1",
"event": "signup",
"user_id": "usr_42"
}
event is one of signup, login, checkout, password_reset, check, or custom:<name> for anything else you want counted. user_id is your own identifier for the person, so Fidro can follow them across IPs and countries. Send user_id on its own and the event defaults to check.
A call without either field is stateless and behaves exactly as it did yesterday.
What you get back
Two new objects under data:
"velocity": {
"ip_events_1h": 3,
"ip_events_24h": 3,
"ip_signups_1h": 3,
"ip_signups_24h": 3,
"subnet_signups_1h": 3,
"ip_distinct_users_24h": 0,
"email_pattern_matches_24h": 1,
"user_distinct_ips_24h": 1,
"user_countries_30d": 1
},
"anomalies": [
{
"code": "ip_signup_burst",
"severity": "high",
"detail": "3 signups from 203.0.113.1 in the last hour."
}
]
The velocity counts include the current call. The anomalies are the rules that fired, each with a severity and a sentence you can show to a reviewer. Every anomaly code also appears in data.checks as true, so code that already loops over the checks picks them up without changes, and the message field gains a "Velocity: ip signup burst." suffix.
The six rules
| Code | Fires when | Effect on the score |
|---|---|---|
ip_signup_burst |
3 or more signups from one IP in an hour, or 5 in 24 hours | +40, recommendation at least challenge |
email_pattern_burst |
3 or more signups in 24 hours whose addresses reduce to one pattern (a.lice+1@, alice2@, al.ice@) |
+40, at least challenge |
impossible_travel |
One user_id seen from two countries within 60 minutes |
+30, at least challenge |
subnet_signup_burst |
5 or more signups from one /24 (or /64) in an hour | +20 |
ip_many_users |
5 or more different user_id values from one IP in 24 hours |
+20 |
new_country_for_user |
A user with 3 or more prior events from one country appears from another | +15 |
These add to the existing email and IP scoring, so a clean address from a clean IP that happens to be the third signup from that IP this hour scores 40 and comes back as challenge. A disposable address in the same burst goes straight to block.
The thresholds are fixed in this release. We would rather ship predictable rules now and add per-account tuning once we have seen how real traffic behaves against them.
Recommendation names
While we were in here we tidied the vocabulary. The /validate recommendation is allow (score 0 to 49), challenge (50 to 79) or block (80 and up). The Stripe chargeback side keeps its own allow, review, refund because the actions there are different. The docs, the SDKs and the MCP tool all use the real names now.
Where you see it
The audit log shows the event type, the user_id and a badge for each anomaly on every stateful call, and the request detail page has a Memory panel with the counts and the anomaly sentences. If you use the MCP server, validate_signup accepts event and user_id too, so "record a login for user 4821 from 203.0.113.9 and tell me if anything looks off" is a complete request.
Storage and privacy
One row per stateful call, scoped to your account and never shared across customers. Rows are deleted when they fall outside your plan's retention window: 7 days on Free, 90 on Starter, 365 on Pro and Enterprise. Test keys write to a separate history, so playing with the API from the playground or a test suite never affects your live counts.
Try it
With a test key, send three signup events from 1.1.1.1 with three different addresses. The third one comes back with ip_signup_burst. The test mode guide has a scenario for every rule, and the Velocity and Anomalies reference explains each count and threshold.
If you are not a customer yet, the free plan includes 200 validations a month, velocity and anomaly detection included, no card required.