Risk assessments
Ask COSS whether a sign-up, login or other event looks like abuse, and why.
Call coss.risk.assess() from your server at the moments abuse happens (a sign-up, a login, a
message, a checkout) and act on the decision it returns:
allow: go ahead.review: suspicious. Let it through with limits (no free credits, email verification first) or hold it for a person. Reviews show up in the dashboard's Risk page and fire arisk.assessment.reviewwebhook.block: refuse it.
Every assessment comes with a score (0-1) and the reasons behind it, so you can see and explain
every decision. COSS combines:
- Email signals: syntax, disposable domains, domains that can't receive mail, role accounts
(
info@), free providers and typos (gmail.con). - IP signals: Tor exit nodes, cloud and hosting networks, networks used by VPNs, private addresses, the network (ASN) and country.
- What this environment has seen: how often the same IP address, subnet, email domain, mailbox
or user came by in the last hour and day; whether the mailbox already has an account (dots and
+tagsfolded at Gmail and similar); and identifiers you reported as fraud. - Your allow and block lists.
Assessing a sign-up
const { : , } = await ..({
: "signup",
: { : . },
: .,
: .,
});
// Fail open: an outage on our side must never lock your users out.
if () .();
if (?. === "block") {
throw new ("We couldn't create your account.");
}
const = ?..(() => . === "email.typo");
if () {
// The one reason worth showing the user: "Did you mean ada@gmail.com?"
.(.);
}
const = await (.);Pass the client's IP address and user agent as your server received them, not your load balancer's
(an ip.private reason means it got the proxy's). Don't show reasons to the person being assessed,
apart from a typo suggestion: they tell an attacker what to change.
Once the account exists, pass its id with later events (user: { id }). Sign-ups that carry a user
id are what duplicate detection compares: a second account for the same mailbox gets
duplicate.canonical_mailbox with the first user's id. If you can choose the id before the account
is created, pass it with the sign-up itself.
const { } = await ..({
: "login",
: { : . },
: .,
: .,
});Other events are message, checkout and custom (with an event_name such as invite_sent).
Velocity is counted per event, so a burst of invitations doesn't count as sign-ups.
Retries and failures
The SDK retries network errors and server errors with the same Idempotency-Key, and a retried
assessment returns the first result without counting the event again. Assessments are limited to
100 per second per API key (bursts of 200).
When a signal is unavailable (a slow DNS lookup, missing IP data), the assessment is made without it
and the group is listed in degraded; it never fails because of it.
What is stored
Assessments keep the email address, IP address and user agent for
90 days, then only keyed hashes of them; decisions, reasons and
signals stay for 1 year. When you delete one of your users, call
coss.risk.users.forget(userId) (see Forgetting users).