Skip to content

Create an event

POST /v1/events decides an agent action before it runs. The SDKs call it from check().

decision is the outcome recorded on the event. It does not change when a review is resolved. finalDecision is the effective outcome: it equals decision when there is no review, stays REVIEW while a review is pending, becomes ALLOW after approval, and becomes BLOCK after denial or expiry. Resolving a review does not emit a new webhook. Poll GET /v1/events/{id}, which returns this same body.

const decision = await vulnify.check({
agent: 'SalesBot',
action: 'EXPORT_DATA',
resource: 'Customer Database',
destination: 'EXTERNAL_EMAIL',
recordsAffected: 12000,
});

X-API-Key may replace the bearer header. See Authentication.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Parameters.
NameInRequiredTypeDescription
Idempotency-KeyheadernostringRetries with the same key (per organization and endpoint, 24 hours) return the stored decision instead of deciding again. The replay always includes finalDecision, derived from the stored decision and the current review.

agent or agentId is required unless the API key is bound to an agent. resource or resourceId is required.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Request body.
FieldTypeRequiredDescription
agentstring (max length 100)noAgent name as registered in Vulnify.
agentIdstring (uuid)noAgent id. Use this or `agent`.
resourcestring (max length 100)noResource name as registered in Vulnify.
resourceIdstring (uuid)noResource id. Use this or `resource`.
destinationINTERNAL | EXTERNAL_EMAIL | EXTERNAL_APIno
recordsAffectedinteger (0–100000000)no
contentstring (max length 100000)noOptional text scanned for sensitive data. Scanned in memory and never stored.
actionREAD_DATA | WRITE_DATA | DELETE_DATA | EXPORT_DATA | SEND_EMAILyes

Unknown JSON fields are rejected (additionalProperties: false). content is scanned in memory and never stored. Its max length is 100000 characters. The whole JSON body must also stay within 200 KB or the API returns 413. The Python client omits keys whose value is None.

HTTP 200 is the decision. additionalProperties is true, so clients should ignore fields they do not know. quotaExceeded means the plan event quota is over the limit and the decision was still made. sandbox is true when the event was created with a TEST key. lgpdCategories is derived from dlpFindings.

{
"id": "00000000-0000-4000-8000-000000000000",
"decision": "REVIEW",
"finalDecision": "REVIEW",
"evaluatedDecision": "REVIEW",
"monitored": false,
"riskLevel": "HIGH",
"riskScore": 80,
"reasons": ["external destination"],
"policy": null,
"dlpFindings": [],
"lgpdCategories": [],
"review": {
"status": "PENDING",
"expiresAt": null,
"decidedAt": null,
"note": null
},
"quotaExceeded": false,
"sandbox": false
}

After an approver accepts that review, a later GET still has "decision": "REVIEW" and "finalDecision": "ALLOW". Denial or expiry sets finalDecision to BLOCK.

An idempotent replay always includes finalDecision. The replay derives it from the stored decision and the current review, including when the stored response predates the field. The same Idempotency-Key applies for 24 hours per organization and endpoint, and the response sets Idempotent-Replay: true. See Authentication.

Decision: { id, decision, finalDecision, evaluatedDecision, monitored, riskLevel, riskScore, reasons, policy, dlpFindings, lgpdCategories, review, quotaExceeded, sandbox }. decision is the stored outcome and does not change when a review is resolved. finalDecision is the effective outcome (ALLOW after approval, BLOCK after denial or expiry, REVIEW while pending) and is always present, including an idempotent replay. quotaExceeded flags a plan overrun without blocking; sandbox is true for TEST keys.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Response body.
FieldTypeRequiredDescription
idstring (uuid)yes
decisionALLOW | REVIEW | BLOCKyesDecision recorded on the event. It does not change when a review is resolved.
finalDecisionALLOW | REVIEW | BLOCKyesEffective outcome. Equals `decision` when there is no review. REVIEW while a review is pending. ALLOW after approval, BLOCK after denial or expiry. Always present, including an idempotent replay of a response stored before this field existed: the replay derives it from the stored decision and the current review.
evaluatedDecisionALLOW | REVIEW | BLOCKyesWhat full enforcement would have decided. Differs from `decision` in monitor mode.
monitoredbooleanyes
riskLevelLOW | MEDIUM | HIGH | CRITICALyes
riskScoreinteger (0–100)yes
reasonsarray of stringyes
policyobject or nullyes
policy.idstring (uuid)yes
policy.namestringyes
dlpFindingsarray of stringyesSensitive-data types found in `content`.
lgpdCategoriesarray of IDENTIFICATION | CONTACT | LOCATION | FINANCIAL | HEALTH | COMPANY | CREDENTIALSyesLGPD categories derived from `dlpFindings`.
reviewobject or nullyesNull when the event has no human review.
review.statusPENDING | APPROVED | DENIED | EXPIREDyes
review.expiresAtstring or null (date-time)yes
review.decidedAtstring or null (date-time)yes
review.notestring or nullyes
quotaExceededbooleanyesPlan quota is over the limit. The decision is still made.
sandboxbooleanyesTrue when the event was created with a TEST key.
OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Response headers.
HeaderDescription
X-Request-IdRequest id for support and log correlation (a caller-sent plain id is kept).
x-api-versionMachine API contract (v1).
x-vulnify-versionAPI build (SemVer, see /changelog).
Idempotent-Replay"true" when the body is a stored replay

The SDKs add degraded: false on this path. degraded is not a field in the OpenAPI response. An outage never returns this body to the SDK. The SDK synthesizes a fallback locally. See Errors.

@vulnify/sdk 0.2.3 requires finalDecision on check() and getEvent(). The API always includes it, including an idempotent replay. vulnify 0.3.0 copies it to final_decision and still types that attribute as optional. Obey finalDecision. While decision stays REVIEW, approval is finalDecision ALLOW. See Node.js and Python.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Error responses.
StatusMeaning
400Validation error (agent/agentId and resource/resourceId are required)
401Missing, invalid, revoked or expired API key
403Key bound to another agent, or caller IP not in the key allowlist
404Unknown agent, resource or gateway route
413JSON body larger than 200 KB
429Rate limit exceeded

A 413 (body over 200 KB) is a client error. The SDKs throw. Fail-open does not apply. See Errors.