Paidwen gives each agent a risk level, with its reasons.
Every 5 minutes, Paidwen computes the risk level of each agent from its permissions and from its decisions of the last 7 days. The level is low, medium, high or critical. It is the highest level among its reasons, and never lower than the risk_level that the owner declares.
The permissions give the exposure.
The SDK names a captured HTTP call by its method and its path, like POST /v1/refunds. Paidwen reads each allowed tool by that form, never by its name.
- Low: the agent may only read, with
GET,HEAD,OPTIONSorTRACE. - Medium: the agent may write with
POST,PUTorPATCH, or call a tool that is not an HTTP call, which counts as a write. An agent in production without a budget is medium too. - High: the agent may delete with
DELETE, a permission allows*, or the agent has no permission at all and may call every tool.
A write or a delete drops out when every decision of the last 7 days on that tool waited for a human or was blocked.
The decisions of the last 7 days give the signals.
- One action refused by a permission or a rule gives medium. Ten refusals in 24 hours give high.
- 80% of a budget spent gives medium. One action refused by the budget gives high.
- An hour with three times more actions than the busiest hour of the 7 days before, and at least 100 actions, gives medium. This signal starts once the agent is 7 days old.
- An open incident, a quarantine or a disabled agent, gives high.
- A high exposure with at least one signal gives critical.
Each reason says what Paidwen saw.
GET /v1/agents/{agent_id} returns the level with its reasons and the time of the computation:
"risk": {
"level": "critical",
"computed_at": "2026-10-09T08:35:00Z",
"reasons": [
{ "code": "delete", "level": "high", "text": "The agent may delete with DELETE /v1/tickets/*." },
{ "code": "refusals", "level": "medium", "text": "1 action was refused in the last 7 days." },
{ "code": "combination", "level": "critical", "text": "The agent has a high exposure and showed a risk signal in the last 7 days." }
]
}GET /v1/agents returns the level of each agent, without the reasons. In the dashboard, the Risk column and its filter read the computed level, and the page of an agent lists its reasons.
A rule reads the level in agent.risk.
{
"name": "Critical agents wait for a human",
"condition": "!has(agent.risk) || agent.risk == \"critical\"",
"verdict": "require_approval",
"mode": "active"
}A new agent has no agent.risk until the next computation, so a security rule plans for the absent field. The level declared by the owner stays in agent.risk_level.
The decisions of a rule that reads agent.risk never count in the level. Without that, a rule that holds the critical agents would lower their level, then stop holding them. Paidwen reads the condition when it saves the rule: a condition that reads every key of agent, or a computed key, counts as reading agent.risk.
The level has six limits.
- It depends on the form of the tools. A
POSTthat only searches counts as a write. - It sees only the actions that go through the SDK.
- The thresholds, 1 and 10 refusals, 80%, three times and 100 actions, are starting values. No source publishes figures for agents: they will be tuned on real journals.
- A new agent has no spike signal for 7 days.
- A level changes at most every 5 minutes.
- The level measures an exposure and observed signals, not a probability of compromise. It never acts alone: only your rules block or hold an action.