The free plugin scores nothing. Every referral it creates gets a neutral verdict — approved, marked degraded — because there is no scorer to ask. Pro attaches one to the same seam, and nothing else about the referral changes shape when it does.
Three signals, not a model#
Pro’s scorer is three weighted rules, not a trained model:
- Self-referral — the buyer is the affiliate.
+0.6. - Velocity — referrals from the same affiliate in the last 24 hours. Over 20,
+0.35. Over 8,+0.2. - Amount — the commission itself. Over 1,000 (in your store’s currency),
+0.15.
The weights add up and clamp at 1.0. At 0.55 or higher the referral is held for review; at 0.85 or higher it is rejected outright, not just flagged. Below 0.55 it is approved. Each fired signal comes back with a plain-language reason — “High velocity: 23 referrals in 24 hours” — so a held commission has an answer attached rather than a number.
It was ported rule for rule from a retired Python service that ran the same three weights over HTTP. Moving it into PHP removed the network hop entirely: the scorer makes no external call, so there is nothing to time out, and nothing to fail because a third-party host was slow to answer.
It fails open#
The free plugin ships the seam this attaches to, and nothing else — no signals, no weights, no thresholds. Ask it to score a referral and it hands back the same answer every time: approved, score 0, marked degraded. That is not a fallback for a broken scorer; it is the entire free behaviour, because there is no scorer.
Once Pro’s filter is attached, a verdict is trusted only if it is an array with a numeric score and an action of approve, review or block. Anything else — a missing field, a score that isn’t a number, an unrecognised action, or the scorer throwing — collapses back to the same neutral, degraded verdict. A misbehaving scorer costs you a data point, never a commission. The scoring call itself is wrapped in a try/catch: a scorer that fatals produces the neutral verdict rather than a broken checkout, which matters because the risk assessment sits on the same code path as recording the referral in the first place.
This is deliberate and not a setting you can flip. Failing closed would mean a scorer outage silently stops crediting every affiliate you have, and you find out from a complaint instead of a log line. A referral that should have been held but wasn’t costs you a review you’d have wanted; a referral that should have been credited but was silently rejected because a scorer hiccupped costs an affiliate their commission and costs you their trust, with no log entry pointing at why.
A score is clamped, not refused#
A scorer answering with something out of range doesn’t get thrown out for that alone — a score of 1.4 is treated as “as risky as it gets” and clamped to 1.0 rather than rejected as malformed, because every threshold downstream expects a 0–1 scale and a clamp is cheaper than a rejection for an add-on that miscalculates a weight. The shape still has to be right — an array with a numeric score and a recognised action — but the number itself is forgiving.
What a verdict actually does#
A verdict decides where a new referral starts, ahead of any amount-based hold you’ve configured. Three things can hold a commission, checked in this order of authority:
- The scorer says block — the referral is rejected outright, whatever the amount.
- The scorer says review — held, whatever the amount.
- The commission clears your review threshold — held, even though the scorer approved it. A threshold of zero disables this check entirely rather than holding everything, because “no threshold set” has to mean “no threshold” — turning the feature off by leaving it blank would otherwise turn it fully on by accident.
Anything left over is approved automatically if you’ve turned on auto-approval; otherwise it’s held. The default is to hold everything, so an existing programme’s behaviour does not silently change the day this ships.
So a score is not only a queue-sorter — a block verdict does reject on its own, without you clicking anything, and a review verdict holds a referral regardless of how small the commission is. What it never does is happen invisibly: every rejected or held referral carries the score and the reasons that produced it, and a degraded verdict is visibly degraded rather than presented as a clean approval. Open a held referral in the free plugin and you’ll see degraded: true with a score of 0 — a plain statement that nothing actually looked at it, not a claim that it passed a check.
Explaining a verdict in words#
Pro can turn a verdict’s reasons into a sentence for you, on demand — never during checkout, only when you’re looking at a flagged referral and want the plain-English version. It goes through WordPress core’s AI Client (wp_ai_client_prompt()), so you pick the provider by installing a connector and supplying your own key. There is no bundled model SDK and no API key of ours in the path. Install no connector and the explanation is simply unavailable — the scorer’s own reasons are still right there, and the referral’s fate was already decided before anything asked a model. The explanation is a convenience layered on top of a decision that has already happened, not a step the decision waits on.