Risk Matrix Scorer
business.risk_matrix
Overview
You send the risks, the likelihood and impact scales they are rated on, and how the two combine — the product of the levels, their sum, or a lookup table you fill in cell by cell. We return each risk's score, the band it falls in, and the action your band declares, plus a count per band for the summary line of a report. A level with no explicit value takes its rank on the ladder, which is what a one-to-five matrix means. A mitigated risk can be multiplied by an adjustment you set, and both the raw and the adjusted score come back so the effect of the mitigation is visible. Bands are checked for gaps and overlaps before anything is scored, and under lookup every cell must be present: a risk with no band would drop out of the report that was meant to escalate it, and a default cell would be our guess at your policy.
Risk matrices live in spreadsheets where the band boundaries are a fill colour and the arithmetic is a formula nobody re-reads. The two failures that follow are always the same: a risk that lands between two bands and quietly gets none, and a matrix that is asymmetric on paper but recomputed as a product in code. Validating the ladder up front and supporting a real lookup table removes both.
Reliability
Fully deterministic: BigDecimal at 34 significant digits, HALF_UP, published at 4 decimal places, no floating point. Bands are half-open — [from, to) — so a score exactly on a boundary belongs to the band above it, and the top band's upper bound is exclusive too, which is why it must sit above the highest score the method can produce. Under LOOKUP the cell's score is used as it stands, never recomputed, so an asymmetric matrix stays asymmetric, and a cell may name a band directly to override the ladder. SCORE_DESC keeps the order you sent for equal scores. Failures: RISK_INPUT_INVALID means a risk or a rule cannot be read — no risks, an id that is empty, too long, padded or carrying a control character, a negative adjustment, or an unaccepted property. RISK_ID_DUPLICATE means a risk id appears twice. RISK_LEVEL_UNKNOWN means a risk or a cell names a level the matrix does not declare. RISK_MATRIX_INVALID means the matrix itself is unusable: fewer than 2 or more than 20 levels, a repeated level or band id, a band that does not rise, cells sent without LOOKUP, a repeated cell, or a cell naming an undeclared band. RISK_CELL_MISSING means the LOOKUP table leaves a combination out. RISK_BANDS_OVERLAP and RISK_BANDS_GAP mean the ladder is not contiguous, or does not reach a score that was produced. RISK_LIMIT_EXCEEDED means more than 2000 risks, 400 cells or 10 bands. None is worth retrying unchanged.
Context footprint
One line per risk plus a small summary, so the response tracks your register size and not the matrix. The lookup cells are not echoed, which keeps a 400-cell request from paying for itself twice. A successful call costs 1 execution credit.
When to use it
When a register must be scored against an agreed matrix, and the bands and actions have to come out the same for everybody.
When you need help rating the risks, or when your scoring is a weighted sum of many criteria rather than a grid.
Scope
Non-goals
- Does not rate the risks: likelihood and impact are your judgement
- Does not carry a built-in matrix, band ladder or convention
- Does not do weighted scoring — this is table lookup and banding, and it shares no engine with business.priority_score or business.decision_matrix
- Does not escalate, notify, assign or store anything
- Does not guess a cell the lookup table leaves out
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response does not contain submitted content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| risks | array | Yes | Bounded array of 1 through 2000 closed risks with unique ids. |
| matrix | object | Yes | Closed likelihood/impact matrix with method, levels, optional cells, and optional bands. |
| rules | object | No | Closed optional scoring rules; omit the object for adjustment 1 and SCORE_DESC order. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| scoredRisks | array | Yes | Bounded array of 1 through 2000 scored risks in the requested sort order. |
| summary | object | Yes | Closed scoring summary with counts, extrema, and the method actually applied. |
| matrix | object | Yes | Closed matrix as actually applied, with resolved level values and bands, without echoing LOOKUP cells. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"business.risk_matrix","input":{"risks":[{"id":"r1","likelihood":"high","impact":"severe"}],"matrix":{"likelihoodLevels":[{"id":"low"},{"id":"high"}],"impactLevels":[{"id":"minor"},{"id":"severe"}],"bands":[{"id":"LOW","from":1,"to":3},{"id":"HIGH","from":3,"to":5}]}}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("business.risk_matrix", {"matrix":{"impactLevels":[{"id":"minor"},{"id":"moderate"},{"id":"severe"}],"bands":[{"id":"LOW","from":1,"to":4,"action":"monitor"},{"id":"MEDIUM","from":4,"to":7,"action":"plan mitigation"},{"id":"HIGH","from":7,"to":10,"action":"escalate"}],"likelihoodLevels":[{"id":"low"},{"id":"medium"},{"id":"high"}]},"risks":[{"id":"r1","likelihood":"high","impact":"severe"},{"id":"r2","likelihood":"high","impact":"severe","mitigated":true},{"id":"r3","likelihood":"low","impact":"minor"}],"rules":{"mitigatedAdjustment":0.5}});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| RISK_INPUT_INVALID | 400 | No | The request payload failed this skill's input validation. |
| RISK_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| RISK_LEVEL_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| RISK_MATRIX_INVALID | 400 | No | The request payload failed this skill's input validation. |
| RISK_CELL_MISSING | 400 | No | The request payload failed this skill's input validation. |
| RISK_BANDS_OVERLAP | 400 | No | The request payload failed this skill's input validation. |
| RISK_BANDS_GAP | 400 | No | The request payload failed this skill's input validation. |
| RISK_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not rate the risks: likelihood and impact are your judgement
- Does not carry a built-in matrix, band ladder or convention
- Does not do weighted scoring — this is table lookup and banding, and it shares no engine with business.priority_score or business.decision_matrix
- Does not escalate, notify, assign or store anything
- Does not guess a cell the lookup table leaves out
- Deciding how likely something is, or how bad it would be
- Weighted multi-criteria scoring — use business.priority_score
- Registers past 2000 risks in one call: split them
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "business.risk_matrix",
"input": {
"matrix": {
"impactLevels": [
{
"id": "minor"
},
{
"id": "moderate"
},
{
"id": "severe"
}
],
"bands": [
{
"id": "LOW",
"from": 1,
"to": 4,
"action": "monitor"
},
{
"id": "MEDIUM",
"from": 4,
"to": 7,
"action": "plan mitigation"
},
{
"id": "HIGH",
"from": 7,
"to": 10,
"action": "escalate"
}
],
"likelihoodLevels": [
{
"id": "low"
},
{
"id": "medium"
},
{
"id": "high"
}
]
},
"risks": [
{
"id": "r1",
"likelihood": "high",
"impact": "severe"
},
{
"id": "r2",
"likelihood": "high",
"impact": "severe",
"mitigated": true
},
{
"id": "r3",
"likelihood": "low",
"impact": "minor"
}
],
"rules": {
"mitigatedAdjustment": 0.5
}
}
}Examples
A three-by-three product matrix with bands, and one risk halved by its mitigation
{
"matrix": {
"impactLevels": [
{
"id": "minor"
},
{
"id": "moderate"
},
{
"id": "severe"
}
],
"bands": [
{
"id": "LOW",
"from": 1,
"to": 4,
"action": "monitor"
},
{
"id": "MEDIUM",
"from": 4,
"to": 7,
"action": "plan mitigation"
},
{
"id": "HIGH",
"from": 7,
"to": 10,
"action": "escalate"
}
],
"likelihoodLevels": [
{
"id": "low"
},
{
"id": "medium"
},
{
"id": "high"
}
]
},
"risks": [
{
"id": "r1",
"likelihood": "high",
"impact": "severe"
},
{
"id": "r2",
"likelihood": "high",
"impact": "severe",
"mitigated": true
},
{
"id": "r3",
"likelihood": "low",
"impact": "minor"
}
],
"rules": {
"mitigatedAdjustment": 0.5
}
}{
"matrix": {
"likelihoodLevels": [
{
"id": "low",
"value": 1
},
{
"id": "medium",
"value": 2
},
{
"id": "high",
"value": 3
}
],
"impactLevels": [
{
"id": "minor",
"value": 1
},
{
"id": "moderate",
"value": 2
},
{
"id": "severe",
"value": 3
}
],
"method": "PRODUCT",
"bands": [
{
"id": "LOW",
"from": 1,
"to": 4,
"action": "monitor"
},
{
"id": "MEDIUM",
"from": 4,
"to": 7,
"action": "plan mitigation"
},
{
"id": "HIGH",
"from": 7,
"to": 10,
"action": "escalate"
}
]
},
"summary": {
"riskCount": 3,
"byBand": {
"HIGH": 1,
"MEDIUM": 1,
"LOW": 1
},
"maxScore": 9,
"minScore": 1,
"method": "PRODUCT"
},
"scoredRisks": [
{
"id": "r1",
"likelihood": "high",
"impact": "severe",
"rawScore": 9,
"adjustedScore": 9,
"band": "HIGH",
"action": "escalate",
"mitigated": false
},
{
"id": "r2",
"likelihood": "high",
"impact": "severe",
"rawScore": 9,
"adjustedScore": 4.5,
"band": "MEDIUM",
"action": "plan mitigation",
"mitigated": true
},
{
"id": "r3",
"likelihood": "low",
"impact": "minor",
"rawScore": 1,
"adjustedScore": 1,
"band": "LOW",
"action": "monitor",
"mitigated": false
}
]
}A lookup table whose corner cell is deliberately not the product of its levels
{
"risks": [
{
"id": "a",
"likelihood": "rare",
"impact": "high"
}
],
"matrix": {
"impactLevels": [
{
"id": "low"
},
{
"id": "high"
}
],
"likelihoodLevels": [
{
"id": "rare"
},
{
"id": "likely"
}
],
"method": "LOOKUP",
"cells": [
{
"likelihood": "rare",
"impact": "low",
"score": 1
},
{
"likelihood": "rare",
"impact": "high",
"score": 5
},
{
"likelihood": "likely",
"impact": "low",
"score": 2
},
{
"likelihood": "likely",
"impact": "high",
"score": 9
}
]
}
}{
"matrix": {
"likelihoodLevels": [
{
"id": "rare",
"value": 1
},
{
"id": "likely",
"value": 2
}
],
"impactLevels": [
{
"id": "low",
"value": 1
},
{
"id": "high",
"value": 2
}
],
"method": "LOOKUP"
},
"summary": {
"riskCount": 1,
"byBand": {},
"maxScore": 5,
"minScore": 5,
"method": "LOOKUP"
},
"scoredRisks": [
{
"id": "a",
"likelihood": "rare",
"impact": "high",
"rawScore": 5,
"adjustedScore": 5,
"mitigated": false
}
]
}Recommended use cases
- Score a risk register the same way every quarter, with the bands published
- Show the effect of a mitigation as a before-and-after score on one line
- Enforce an agreed matrix — including an asymmetric one — across several teams
- Produce the count-per-band figures a risk report opens with
Avoid for
- Deciding how likely something is, or how bad it would be
- Weighted multi-criteria scoring — use business.priority_score
- Registers past 2000 risks in one call: split them
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.