Skip to content
Browse documentation

Interpreting risk signals

This page explains what the risk_signals score measures and how far to trust it, especially when some evidence is missing. For field definitions and the exact condition behind each flag, see the risk signals reference.

What the score is

The score is a sum. Each flag has a fixed number of points, and score adds up the points of every flag that was confirmed (triggered) in this assessment, capped at 100. Nothing else feeds it: no model, no weighting by sector, no adjustment for sources that were not checked.

The weights are absolute values, not shares of a total. The same set of triggered flags always produces the same number, whichever sources your key can use and whichever sources answered. That keeps your thresholds stable over time. New flag codes can be added, so a company can gain points from a check that did not exist before, but an existing combination never changes its score.

Because of how it is built, the score is not several things people often read into it:

  • It is not a credit score. It says nothing about ability to pay.
  • It is not a probability. A score of 40 does not mean a 40% chance of anything.
  • It is not a compliance decision. It tells you which registry facts warrant review. Your policy decides what to do about them.

Why the score is a floor, never a cap

Every flag depends on one or more sources. When a source answers, the flag is either triggered or clear. When a source fails, the flag cannot be checked, and the API says so: the flag is listed in indeterminate instead of disappearing.

This matters because the alternative is silent. If a failed insolvency check simply contributed nothing, a company in liquidation could come back with a low score that looks like a clean bill of health. A missing source never lowers the score. It leaves the score where the confirmed evidence puts it and marks the rest as unknown.

So when score_is_floor is true, read score as the least risk the evidence confirms, not the most. potential_score is the other end: the score if every indeterminate flag turned out to be triggered, also capped at 100. Together they give you a range. A company with score 20 and potential_score 70 has one confirmed warning and an unchecked critical, which is a very different position from a company scoring 20 with nothing unknown.

The width of the range depends on which source is missing. If the officers check is unavailable, four flags become indeterminate and potential_score can sit 75 points above score. If the Companies House company profile fails, every flag that depends on it becomes indeterminate, and the score is close to meaningless until you retry.

The severity of an indeterminate entry is the severity the flag would carry if triggered. That gives you a simple gate that does not depend on the number at all: if any indeterminate entry is critical, the assessment is not finished.

Coverage: what was actually assessed

coverage lists the risk-bearing sources that were consulted successfully for this score. A source absent from coverage was not assessed, and the flags that depend on it tell you nothing. There are three different reasons a source can be absent, and they lead to different decisions.

The source failed. The flags that need it appear in indeterminate with reason source_unavailable, and score_is_floor is true. This is usually temporary. Retrying later is the right response.

You did not request the source. If you call the entity lookup with ?sources=ch, the insolvency check does not run. Its flag is not indeterminate, because nothing went wrong: you scoped it out. It simply appears nowhere, and insolvency is missing from coverage. This is the case most likely to mislead you, because score_is_floor stays false. If your policy depends on a dimension, check that its source is in coverage rather than relying on the floor flag. The /risk endpoint avoids this by always requesting the full set of risk-bearing sources.

The source is switched off. A source disabled across the platform is never requested, so it behaves like a source you did not ask for. Marking it indeterminate on every response would keep score_is_floor permanently true and teach integrations to ignore it.

Today only source_unavailable appears in indeterminate. The not_requested and feature_disabled reason values are reserved so these cases can be reported explicitly later without a breaking change. Until then, coverage is where you see them.

Partial responses are not errors

If a source fails during an entity lookup, you still get 200 with everything that did resolve. The failed source is named in sources_failed, and its risk flags move to indeterminate. The response is a partial response, not a failure, and you should handle it as data with a known gap rather than retrying blindly or discarding it.

The /risk endpoint does not return sources_failed. It does not need to: indeterminate and coverage carry the same information inside risk_signals, so the risk payload stands on its own.

A source can also answer without settling a flag. If the officers register resolves but some directors could not be checked for disqualification, only DIRECTOR_DISQUALIFIED becomes indeterminate. The officers source stays in coverage, because the rest of its flags were evaluated. A confirmed disqualification always counts, even when other directors are unchecked.

One case is a refusal rather than a partial response. When the shared Companies House capacity is exhausted, the core company fetch is refused and you get 429 upstream_capacity. The error codes page covers how to retry.

Severity and points answer different questions

Points tell you how much confirmed evidence has accumulated. Severity tells you how serious each finding is on its own. A single critical flag can sit below a threshold that several minor issues together exceed. A threshold on score alone will treat a disqualified director more leniently than three administrative lapses.

The combinations below use the current weights. Compare the second and third rows.

Triggered flagsScoreHighest severityReading
OUTSTANDING_CHARGESALL_ASSETS_CHARGED25warningA whole-undertaking debenture. Routine for a financed business.
DIRECTOR_DISQUALIFIED40criticalOne finding, serious on its own.
ACCOUNTS_OVERDUECONFIRMATION_OVERDUENO_PSC_RECORDED45warningFiling failures and no recorded controller. Higher total, no critical.
INSOLVENCY_ACTIVEACCOUNTS_OVERDUE70criticalFormal insolvency with a filing failure.
INSOLVENCY_ACTIVEACCOUNTS_OVERDUECONFIRMATION_OVERDUEOUTSTANDING_CHARGESALL_ASSETS_CHARGED100 (110 capped)criticalThe cap hides how far past 100 the points go.

A gate that respects both reads all three signals. It sends a company to review when the score reaches your threshold, when any triggered flag is critical, or when score_is_floor is true and any indeterminate entry is critical. Match on the flag code, never on its description.

A high score is a prompt for human review, not proof of wrongdoing. Outstanding charges, for example, are normal for any business with a bank loan. Equally, a score of 0 with full coverage only means none of these specific checks fired.

Control graphs and incomplete evidence

A corporate person with significant control is an ordinary registry fact. It adds no flag and no points to the subject. When ch_psc.corporate_psc_count is above zero and ch_psc.control_graph_available is true, you can request the control graph separately to see the companies behind and around the subject.

The graph reports RELATED_COMPANY_DISSOLVED and RELATED_COMPANY_INSOLVENCY in related_risk.findings. These are context about neighbours, not findings against the subject, and they never change its score. A dissolved sister company is worth knowing about. It does not make the subject dissolved.

The harder question is what an empty findings list means. The graph is bounded: it stops at depth, node, request and time limits, and some companies it reaches may stay unresolved or unassessed. Three fields tell you how much of the picture you have:

  • related_risk.status is assessed only when discovery and assessment both completed. Otherwise it is partial.
  • The graph's coverage says whether discovery and assessment completed, and how many companies were and were not assessed.
  • frontier lists where the traversal stopped and why, for example depth_limit, node_limit, request_budget, deadline or unresolved_identifier.

No findings with incomplete coverage does not clear the group. An unassessed company could be dissolved or insolvent, and a company beyond the frontier was never seen. Treat that state the way you treat a score floor: as missing evidence, not good news.

Even a complete graph has limits. It is complete for a bounded traversal of corporate PSC records at the vintage it reports. It does not follow natural persons or ceased PSCs, and it does not join foreign or ambiguous identifiers by name. It cannot establish every owner, every ultimate beneficial owner or every liability relationship.

Related