Quickstart
This guide authorizes one action: an agent is about to export customer records to an external destination. The export runs only when Vulnify returns ALLOW.
These samples match @vulnify/sdk 0.3.0 and vulnify 0.4.0. On Node.js, check() and getEvent() require finalDecision. To keep policies in git, use the CLI. The Python CLI is pip install "vulnify[cli]".
-
Create an API key
Sign in at app.vulnify.io. Owners and admins create keys under API keys. Copy the key when it is shown. Vulnify stores only a hash, so it cannot be shown again.
Use a
TESTkey while you are trying the API. Test events are sandbox events: they stay out of dashboards and usage. Details are on API keys. -
Install an SDK
The Node.js SDK needs Node.js 18 or newer. The Python SDK needs Python 3.9 or newer and uses only the standard library.
Terminal window npm install @vulnify/sdkTerminal window pip install vulnifyNo install. You will call
https://api.vulnify.io/v1/eventsdirectly. -
Ask before the export
Set
VULNIFY_API_KEY. Both SDKs callhttps://api.vulnify.iowhen you omit the base URL. On Node.js, passbaseUrl: process.env.VULNIFY_BASE_URLto point at another host. The constructor does not read that variable itself. On Python,VULNIFY_BASE_URLis used whenbase_urlis omitted, and an explicitbase_urlwins.import { Vulnify } from '@vulnify/sdk';const apiKey = process.env.VULNIFY_API_KEY;if (!apiKey) {throw new Error('Set VULNIFY_API_KEY');}const vulnify = new Vulnify({apiKey,baseUrl: process.env.VULNIFY_BASE_URL,});/** Replace the body with the real export. It runs only after ALLOW. */async function exportCustomerRecords(): Promise<void> {console.log('exporting customer records to the external destination');}const decision = await vulnify.check({agent: 'SalesBot',action: 'EXPORT_DATA',resource: 'Customer Database',destination: 'EXTERNAL_EMAIL',recordsAffected: 12000,});const outcome = decision.finalDecision;if (outcome === 'ALLOW') {await exportCustomerRecords();} else if (outcome === 'REVIEW') {const reasons = decision.reasons.join('; ') || 'no reason given';const score = decision.riskScore == null ? 'unknown' : String(decision.riskScore);throw new Error(`A human must approve this export before it runs (event ${decision.id ?? 'none'}, score ${score}). ${reasons}`,);} else if (decision.degraded) {throw new Error(`Vulnify could not be reached. The export was not run. ${decision.reasons.join('; ')}`);} else {throw new Error(`Export blocked. The export was not run. ${decision.reasons.join('; ')}`);}import osfrom vulnify import Vulnifydef export_customer_records() -> None:"""Replace the body with the real export. It runs only after ALLOW."""print("exporting customer records to the external destination")api_key = os.environ.get("VULNIFY_API_KEY")if not api_key:raise SystemExit("Set VULNIFY_API_KEY")vulnify = Vulnify(api_key=api_key)decision = vulnify.check(agent="SalesBot",action="EXPORT_DATA",resource="Customer Database",destination="EXTERNAL_EMAIL",records_affected=12000,)outcome = decision.final_decision if decision.final_decision is not None else decision.decisionif outcome == "ALLOW":export_customer_records()elif outcome == "REVIEW":reasons = "; ".join(decision.reasons) or "no reason given"score = "unknown" if decision.risk_score is None else str(decision.risk_score)event_id = decision.id or "none"raise SystemExit(f"A human must approve this export before it runs (event {event_id}, score {score}). {reasons}")elif decision.degraded:raise SystemExit(f"Vulnify could not be reached. The export was not run. {'; '.join(decision.reasons)}")else:raise SystemExit(f"Export blocked. The export was not run. {'; '.join(decision.reasons)}")Terminal window curl -X POST https://api.vulnify.io/v1/events \-H "Authorization: Bearer $VULNIFY_API_KEY" \-H "Content-Type: application/json" \-H "Idempotency-Key: $(uuidgen)" \-d '{"agent": "SalesBot","action": "EXPORT_DATA","resource": "Customer Database","destination": "EXTERNAL_EMAIL","recordsAffected": 12000}'Run the export only when the JSON
finalDecisionfield isALLOW.decisionstaysREVIEWafter a review is resolved. cURL does not apply the SDK fail-closed fallback. If the request errors, do not run the export.
SalesBot and Customer Database must be the agent and resource names registered in your organization. An unknown agent or resource, a rejected payload, an invalid API key, or a body over 200 KB (HTTP 413) raises an error. Those errors are not turned into ALLOW when fail-open is set. Run the export only when finalDecision is ALLOW. Right after check(), that matches decision unless a review is already resolved. A later read of the same event can keep decision as REVIEW while finalDecision becomes ALLOW. See Decisions.
The same check, with guard(), is shorter. guard() runs your function only for ALLOW and throws for REVIEW and BLOCK. See Node.js and Python.
Decision, anomaly, and test notifications arrive as signed HTTP POSTs. The event list, headers, and signature check are on Webhooks.

