How scoring works
Every call gets one number between 0 and 1, where we would draw the line on it, and a list of reasons. Those three things are the whole interface.
The score
A score is a number from 0 to 1. Zero is a call we have nothing against. One is as bad as it gets. It is a judgement about this call to this number right now, not a permanent property of the caller. The same number can score differently an hour later, and a number that behaves goes back down on its own.
0.5 is where we start to doubt a call and 0.8 is where we would refuse it, and those two numbers come back with every score. A line you draw on the score today means the same thing next year. What goes into the score, the weights and the windows, is not published and moves as we see more traffic.The recommended action
action is where we would draw the line on the score, at the thresholds that come back with it. Take it as it is and you are making the decision we would make. Draw your own line on the score, a stricter one on a sales line or a looser one on support, and action becomes a second opinion.
- allow"allow"
- Connect it. Nothing here worries us.
- review"review"
- Something is off, but not enough to refuse a caller who might be real. Connect it and watch it; this is the band where a short verification question earns its keep.
- reject"reject"
- Do not connect it. On Guard, if we are connected to your platform, we end it for you; at the gate, the caller is challenged.
"score": 0.92,
"action": "reject",
"thresholds": { "review": 0.5, "reject": 0.8 }If you are drawing your own line, the shape that works is one number for refusing and, if you want it, a lower one for a verification step. Anything under the lower one connects as usual.
The reasons
Every score carries a reasons array of short stable strings. They are there so a human reading an alert at 3am knows what happened, and so your logs say something useful. Treat them as labels to display and group by, not as an interface to branch on, because the set grows.
| Code | In a sentence |
|---|---|
| blocked_manual | You blocked this number yourself, from the dashboard or the API. |
| blocked_exact | We placed a temporary block on this exact number after earlier behaviour. |
| blocked_stem | A block on a run of consecutive numbers this one belongs to. |
| blocked_volume | Your protected number is over its hourly volume and is refusing new callers for a while. |
| exact_rate | This number has called you more than a number should in the time it had. |
| stem_rate | Calls arriving from a run of numbers that differ only in the last digits: one caller with a block of lines. |
| prefix_cluster | An unusual amount of your traffic suddenly sharing a prefix. |
| country_burst | A burst from a single country, measured against your own normal. |
| number_volume | The protected number itself is taking far more calls than it usually does. |
| geo_cluster | Many different numbers with no history that the carrier places in the same small area. |
| attestation_failed | The carrier's own attestation says the calling number is not verified. |
| seen_on_network | Other protected numbers, at other companies, are hearing from this same number right now. |
| stem_on_network | As above, for the run of numbers this one sits in. |
| bad_history | This caller has a record with you specifically. |
| trusted | On your trusted list. Scores zero and stops there. |
| daily_cap | Your plan's daily scoring allowance is spent. The call is allowed and recorded, not scored. |
| live_* | Added during the call by the live monitor: live_recording_likely, live_barge_storm and the rest of the live flags. |
The two scores
There are two numbers on every finished call, and they answer different questions.
- scorenumber, 0–1
- The ring-time score. Decided before your agent speaks, from who is calling and what the numbers around it are doing. It can rise during a call as the live monitor trips flags, never fall. This is what
actionfollows. - spam_scoreinteger, 0–100
- The call's own score, settled once it has ended: how it behaved (interruptions, the length of the caller's turns, how long it ran) plus what the number was doing when it rang and its record before. Under your
spam_thresholdreads as a normal call. It is what the dashboard colours, and what feeds the number's history for next time.
Why other people's traffic matters
The two *_on_network reasons are the ones you cannot build yourself. A number that has just started working through a list hits fifty companies before it hits you twice. On its second call to you it looks like nothing; across everyone we protect it is already unmistakable.
What crosses between accounts is a count and a fading score per calling number. Never who was called, never by whom, never anything about the conversation. When an account leaves, its contribution stays anonymous and its link to it is dropped.
What it looks like
{
"caller": "+15125550142",
"number": "+14155550100",
"direction": "inbound",
"score": 0.92,
"action": "reject",
"reasons": ["stem_rate", "seen_on_network"],
"block": null,
"level": "standard"
}A run of consecutive numbers is working your line, and the same caller is on other people's numbers at the same time. See the score endpoint for how to ask this question, and reputation for the longer story behind one number.