Risks¶
Create, list, inspect, and delete risk records - the work-tracking layer for compliance and security findings - together with the tasks attached to them.
Use cases¶
- Raise a risk programmatically from an external scanner or rule engine.
- Mirror the risk queue into an external work-tracking system, fetching only what changed since the last run (
editedSince). - Pull every risk from one source, e.g. all threat-model or all SARIF risks (
origin). - Show an auditor who changed a risk, when and how (
/history). - Record a documented decision - accept, transfer, a temporary exception until a date - with its rationale.
- Report the risk stock and how it develops, or export the register to a spreadsheet (
/reports/*,format=csv). - Progress tasks on a risk as engineering work lands.
Operations¶
| Method | Path | What it does | Scopes |
|---|---|---|---|
| GET | /core/risks |
List risks | manager, compliance_manager, portfolio_manager, company_security_manager |
| POST | /core/risks |
Create risk | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| GET | /core/risks/{risk_id} |
Get risk details | manager, compliance_manager, portfolio_manager, company_security_manager |
| POST | /core/risks/{risk_id} |
Update risk | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| PATCH | /core/risks/{risk_id} |
Update risk (PATCH) | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| DELETE | /core/risks/{risk_id} |
Delete risk | manager, account_admin, enterprise_admin, portfolio_manager, company_security_manager |
| GET | /core/risks/reports/inventory |
Risk inventory report | manager, compliance_manager, portfolio_manager, company_security_manager |
| GET | /core/risks/scheme |
Resolved risk assessment scheme | manager, compliance_manager, portfolio_manager, company_security_manager, developer |
| GET | /core/risks/reports/trend |
Risk development report | manager, compliance_manager, portfolio_manager, company_security_manager |
| GET | /core/risks/{risk_id}/history |
Get risk history | manager, compliance_manager, portfolio_manager, company_security_manager |
| GET | /core/risks/{risk_id}/tasks |
List tasks by risk | manager, compliance_manager, portfolio_manager, company_security_manager |
| POST | /core/risks/{risk_id}/tasks |
Create task | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| GET | /core/risks/{risk_id}/tasks/{task_id} |
Get task details | manager, compliance_manager, portfolio_manager, company_security_manager |
| POST | /core/risks/{risk_id}/tasks/{task_id} |
Update task | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| DELETE | /core/risks/{risk_id}/tasks/{task_id} |
Delete task | manager, account_admin, enterprise_admin, portfolio_manager, company_security_manager |
| GET | /core/risks/{risk_id}/countermeasures |
List countermeasures by risk | manager, compliance_manager, portfolio_manager, company_security_manager |
| POST | /core/risks/{risk_id}/countermeasures |
Create countermeasure | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| GET | /core/risks/{risk_id}/countermeasures/{cm_id} |
Get countermeasure details | manager, compliance_manager, portfolio_manager, company_security_manager |
| POST | /core/risks/{risk_id}/countermeasures/{cm_id} |
Update countermeasure | developer, manager, portfolio_manager, compliance_manager, company_security_manager |
| DELETE | /core/risks/{risk_id}/countermeasures/{cm_id} |
Delete countermeasure | manager, account_admin, enterprise_admin, portfolio_manager, company_security_manager |
Request and response schemas are in the OpenAPI specification, which is also available as interactive documentation in the app.
Listing and paging¶
GET /core/risks returns the whole company's risks unless projectId or moduleId narrows it. Filters combine:
| Parameter | Values |
|---|---|
state |
One state or a comma-separated list, e.g. identified,assessed. The nine states: identified, assessed, approved, planned, implementing, deferred, escalated, monitored, closed. |
treatment |
untreated, in-treatment, done, or a comma-separated list, e.g. untreated,in-treatment for every open risk. See Treatment strategy and state. |
impactClass |
An impact class of the company's risk assessment scheme (by default low, moderate, high, critical, existential), or a list. |
origin |
SDLC, SQ, SSC, CVE, YARA, CWE, TD, TMA Assessment, LIC, threat-model, or a list. |
createdSince, editedSince |
ISO 8601 date or date-time, inclusive. |
projectId, moduleId |
Scope to a project (with its modules) or a module. |
page |
Page number from 1; 100 risks per page. |
state, impactClass and origin also accept none, which selects risks without that value - the "unset" bucket of the inventory report. treatment does not. An unknown value or a malformed date answers 400 with {"error": ..., "parameter": ...}, never an empty list; so does a deprecated state (?state=accepted), with the error listing the nine states plus none.
The body stays an array. Paging metadata is in headers: X-Total-Count (matches across all pages) and X-Has-More (true while another page exists). GET /core/risks/{risk_id}/history pages the same way.
Treatment strategy and state¶
A risk answers two questions in two fields (since 3.3.28):
riskProperties.generalApproachis the treatment strategy - what was decided:avoid,mitigate,transferoraccept, or empty while nobody has decided.riskProperties.stateis the progress:identified,assessed,approved,planned,implementing,deferred,escalated,monitoredorclosed.
Every risk has one treatment class, derived from both and never stored:
| Class | When |
|---|---|
untreated |
No strategy, or the state is empty, identified, assessed or not one of the nine. |
in-treatment |
avoid or mitigate with a state from approved to monitored (including deferred and escalated), or accept / transfer with deferred or escalated. |
done |
The state is closed, or accept / transfer with approved, planned, implementing or monitored. |
The API does not refuse a contradictory pair such as accept + implementing; it classifies by the rules above (done). The UI only offers approved and closed with accept and transfer. riskProperties.resolvedAt is set when a risk becomes done and cleared when it stops being done, a strategy-only change included; stateChangedAt moves only when the state changes.
Deprecated values. Writes (POST / PATCH /core/risks) still accept the pre-3.3.28 values and translate them: state accepted becomes accept + approved, transferred becomes transfer + approved, eliminated becomes avoid + closed, out-of-scope becomes accept + closed, expired becomes closed; strategy resolve becomes avoid, ignore becomes accept (and an undecided state becomes approved). A state value only fills the strategy when the request and the stored risk have none, so {"state": "accepted", "generalApproach": "mitigate"} stores mitigate + approved. These values will be removed in a future release. A value that is neither current nor deprecated answers 400 naming the field. List filters do not accept deprecated values.
A temporary exception whose validUntil has passed moves its risk back to assessed for a new decision; strategy and decision are kept.
Decisions¶
riskProperties.decision records what was decided about a risk: type (mitigate, transfer, accept, temporary-exception, out-of-scope), a rationale of 10-2000 characters, and optional validFrom / validUntil. A new decision on a risk without a strategy sets it: mitigate to mitigate, transfer to transfer, and accept, temporary-exception and out-of-scope to accept. A temporary exception needs validUntil, which may not lie in the past when written. The server sets decidedBy, decidedByName and decidedAt whenever the decision's content changes; values sent for them are ignored. Send the stored decision back unchanged and it keeps its attribution; send null to remove it. An invalid decision answers 400 with {"error": ..., "parameter": "riskProperties.decision.<field>"}.
Risk assessment scheme¶
Impact class, BV_enum, EF_enum and ARO_enum must be values of the risk assessment scheme that applies to the risk's module or project (configured under Administration → Policies → Risk assessment, and on a project's or module's CIA tab). Without a configured scheme the built-in values apply. A value that is written or changed and is not in the scheme answers 400 with {"error": ..., "parameter": "riskProperties.<field>"}; a stored value the request leaves unchanged is accepted even if the scheme no longer contains it.
The treatment strategy is not part of the scheme: it is always one of the four values above.
The scheme's thresholds are enforced on the same write: a risk at or above the "decision required" class cannot become done without a decision (riskProperties.decision); a risk at or above the "acceptance not permitted" class cannot get an accept or temporary-exception decision (riskProperties.decision.type) or the strategy accept (riskProperties.generalApproach); and a temporary exception cannot run past the maximum number of days. Each answers 400 naming the field. A change of the strategy alone is checked too.
Reading the scheme¶
GET /core/risks/scheme?projectId=... or ?moduleId=... returns the scheme that write enforcement applies to that project or module, so a client can check its values before it writes. Keys with manager, compliance_manager, portfolio_manager, company_security_manager or developer may read it.
- Precedence.
moduleIdwins: the module's own project decides the project level, and aprojectIdsent with it is ignored. An empty parameter counts as absent. 400with{"error": ..., "parameter": ...}: neither parameter given (parameter: "projectId"); a value that is not one id (a repeated parameter, bracket syntax, more than 64 characters);format=csv, since the scheme is JSON only.404"Resource not found: <id>": amoduleIdthat is not a module of the company, even with a validprojectId, or aprojectIdalone that is not a project of the company. There is no fallback to the company scheme.
These rules are deliberately stricter than the list, which is company-wide without a scope: a client must not check its values against rules that are not the ones enforced.
The response:
| Field | Content |
|---|---|
scope |
{projectId, moduleId} the scheme was resolved for; with a module, projectId is the module's project, or null when that project is not in the company. |
scheme |
impactClasses (value, label, rank, ordered by rank), valueScale (for BV_enum and EF_enum), aroScale, thresholds (decisionRequiredFrom, acceptanceForbiddenFrom, maxExceptionDays, each null when switched off), and the fixed treatmentStrategies and states (value, label). |
sources |
Where each list and each threshold comes from: module, project, company, default (built in), or fixed for treatmentStrategies and states. |
fields |
Which list each risk field is checked against, e.g. "ARO_enum": "aroScale", "generalApproach": "treatmentStrategies". |
A threshold that names an impact class the scale no longer has does nothing: no risk is at or above it.
Proposed assessment¶
Risks created by a SARIF import, the vulnerability check or a manual license resolution carry riskProperties.assessmentInputs (source, findingId, severity: {value, rank}, exploitability: {epss} when known, context: {CR, IR, AR}), proposedImpactClass and assessmentOrigin (proposed while the impact class is the machine's proposal, human once somebody changed it). All three are read-only: values sent in a request body are ignored. Changing impactClass sets assessmentOrigin to human, and later imports then leave the impact class alone.
Risks from vulnerabilities¶
A project with autoCreateCveRisks (see Projects) keeps one risk per vulnerability and module. Such a risk has origin CVE, riskProperties.autoKey cve|<moduleId>|<vulnerability id> and riskProperties.autoCve (the vulnerability id: CVE, GHSA, OSV, ...). riskProperties.sourceRefs lists the affected components (componentKey, versions); when none reports the vulnerability any more it is empty and sourceRefsPrevCount holds the previous count. riskProperties.vexEvidence holds what the last published VEX advisory stated for the vulnerability and module: csafId, trackingId, version, publishedAt, status (known_not_affected, known_affected, fixed, under_investigation), justification, remediationCategory. All of these are server-owned: values sent in a request body are ignored. How and when the state moves is described in Risks from vulnerabilities (CVE).
Reports and CSV¶
Both reports take the same projectId, moduleId, state, origin and treatment filters as the list and are company-wide without them. The inventory counts total, untreated, inTreatment, open (untreated + inTreatment) and resolved (the done class), so open + resolved = total; each class count equals the length of GET /core/risks?treatment=<class> with the same filters. In the trend, resolved counts risks that became done. The trend needs from; to defaults to now, granularity to month (or week), and a range may span at most 60 months or 261 weeks. Buckets are UTC calendar months or ISO weeks, [start, end). Risks resolved before 3.3.23, and risks that became done through the 3.3.28 migration without a recorded state change, have no resolution date; the trend counts them in unknownResolutionDate instead of guessing a bucket.
format=csv on either report, and on GET /core/risks, returns text/csv (RFC 4180, header row). On the list it exports the whole register - every matching risk, up to 50,000 - rather than one page; its generalApproach and treatment columns follow state. Cells that a spreadsheet would run as a formula are prefixed with '.
Authentication¶
Send your key in the x-api-key header. The Scopes column shows which key scopes may call each operation; a key with another scope gets 403. See API scopes.
Related¶
- Risks - UI-side documentation
- Compliance Approvals - risks can block approvals
- Reports - risk reports and dashboards
- Endpoint groups