Skip to content

Decisions

Every check returns a decision. Run the action only when finalDecision is ALLOW.

finalDecision What you do
ALLOW Run the action.
REVIEW Do not run the action. A review is pending.
BLOCK Do not run the action.

decision is the outcome recorded on the event. It does not change when a review is resolved. When there is no review, finalDecision equals decision. While a review is pending, finalDecision is REVIEW. After approval it is ALLOW. After denial or expiry it is BLOCK.

riskScore is an integer from 0 to 100. reasons explains the score. riskLevel is LOW, MEDIUM, HIGH, or CRITICAL.

Risk scores, policies, and decisions are automated and rule-based. They are an aid. Vulnify does not guarantee that every harmful action is detected or blocked, or that every allowed action is harmless. You stay responsible for your agents, your data, and whether you fail open or fail closed.

The JSON body uses camelCase. The Python SDK maps those names onto snake_case attributes. finalDecision is required by the API, including on an idempotent replay. @vulnify/sdk 0.2.3 requires it on check() and getEvent(). vulnify 0.3.0 copies it to final_decision and still types that attribute as optional. See Node.js and Python.

JSON field Python attribute Meaning
id id Event id. null on an SDK fallback, when the API was not reached.
decision decision Recorded outcome. Stays REVIEW after the review is resolved.
finalDecision final_decision Effective outcome. See the table above. Required on @vulnify/sdk 0.2.3 check() and getEvent().
evaluatedDecision evaluated_decision What full enforcement would have returned.
monitored monitored true when the event was recorded and not enforced.
review review Present for a human review. See Reviews.
riskLevel risk_level LOW, MEDIUM, HIGH, or CRITICAL.
riskScore risk_score Integer 0–100.
reasons reasons Strings explaining the decision.
policy policy { "id", "name" }, or null.
dlpFindings dlp_findings Sensitive-data types found in content.
lgpdCategories lgpd_categories LGPD categories for those findings: IDENTIFICATION, CONTACT, LOCATION, FINANCIAL, HEALTH, COMPANY, CREDENTIALS.
quotaExceeded quota_exceeded true when the organization is over its event quota. The decision is still made.
sandbox sandbox true when the event was created with a TEST key.

degraded (Python: degraded) is added by the SDKs. It is true only when the SDK applied the fail-closed or fail-open fallback because Vulnify could not be reached. A normal API response is not degraded. The OpenAPI response does not include degraded.

The Node.js client fills dlpFindings, lgpdCategories, quotaExceeded, and sandbox when an older server omits them, and leaves finalDecision absent. The current machine contract requires those fields on both POST /v1/events and GET /v1/events/{id}. Python copies them, using False for quota_exceeded and sandbox and an empty list for lgpd_categories when the keys are missing. final_decision stays None when finalDecision is missing.

Monitor mode records what would have been blocked or reviewed and answers ALLOW. Use it to see how rules behave before you enforce them. monitored is true, and evaluatedDecision is the decision enforcement would have returned. Follow finalDecision (which matches decision when there is no review), not evaluatedDecision.

failMode / fail_mode defaults to closed.

A timeout, a network error, HTTP 408, HTTP 429, or HTTP 5xx, after retries, becomes:

  • decision: BLOCK (or ALLOW if you set fail-open)
  • degraded: true
  • id: null
  • reasons: a string that starts with Vulnify unavailable and includes the fail mode

The Node.js SDK says failMode=closed or failMode=open. The Python SDK says fail_mode=closed or fail_mode=open.

Set fail-open only when an outage should let the action through. An invalid API key, an unknown agent or resource, a rejected payload, or a body over 200 KB (HTTP 413) still throws. Fail-open does not swallow those errors. See Errors.

Retries reuse one Idempotency-Key, so a retry does not create a second event. The default is 2 retries (3 attempts) and a 3 second timeout per attempt.