Concepts
Decisions
Every check returns a decision, and every decision has one of three outcomes. It's the only field your app has to act on.
Generate. Nothing in your policy stopped it. It may carry tags, like adult, for you to label, filter or age-gate the result.
Hold it. A person should look first. The prompt is waiting in your review queue.
Don't generate. Tell the user it isn't allowed. Nothing was rendered, so there's nothing to take down.
In your app
// decide.mjs · Node 18+ · VINDEX_KEY=vx_test_... node decide.mjs "a prompt"
const prompt = process.argv[2] ?? "a lighthouse at dusk, oil painting";
const res = await fetch("https://api.getvindex.com/v1/check", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.VINDEX_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ input: { prompt } }),
});
if (!res.ok) throw new Error(`Vindex ${res.status}: ${await res.text()}`);
const decision = await res.json();
if (decision.shadow_mode) {
console.log("shadow mode: log it, enforce nothing", decision.outcome);
} else if (decision.outcome === "allow") {
console.log("generate", decision.tags);
} else if (decision.outcome === "review") {
console.log("hold until a person rules on", decision.id);
} else {
console.log("refuse", decision.id);
}shadow_mode is true while your workspace is in shadow mode: log those decisions, don't enforce them yet.
How the outcome is chosen
Your policy gives every check two lines and an action for each. A check whose score reaches the act line takes the first action (on_act); one that reaches only the review line takes the second (on_review). Below both, it does nothing.
The actions are block, review, tag and allow. The decision takes the most severe action any check chose: block over review over allow. A tag counts as allow, and every tag applied lands in tags.
Four real decisions
Same four checks, two presets. Numbers from the live API with a test key.
| Prompt | Policy | Outcome | Why |
|---|---|---|---|
| a lighthouse at dusk, oil painting | adult-platform | YES · allow | No check reached its review line. |
| a woman in lingerie, boudoir photo, soft window light | adult-platform | YES · allow | adult 0.91 reached its act line, 0.70: tag adult. |
| a woman in lingerie, boudoir photo, soft window light | family-app | NO · block | adult 0.91 reached its act line, 0.35: block. |
| Abraham Lincoln giving a speech, black and white photograph | adult-platform | HMM · review | real_person 0.99 reached its act line, 0.50: review. |
The block, as the API returned it:
{
"id": "dec_60b500b8ab8d4b5882530f2ea8115f14",
"outcome": "block",
"tags": [],
"checks": {
"minors_sexual": {
"score": 0.0255,
"review_score": 0.03,
"fired": []
},
"adult": {
"score": 0.91,
"review_score": 0.91,
"fired": [
"adult_nsfw"
]
},
"real_person": {
"score": 0.02,
"review_score": 0.02,
"fired": []
},
"copyrighted_ip": {
"score": 0.02,
"review_score": 0.02,
"fired": []
}
},
"policy": {
"id": "pol_69ea6e25ab524a98944b8e83407df74a",
"version": 1
},
"engine_version": "2026.09",
"latency_ms": 376,
"dry_run": false,
"mode": "test",
"shadow_mode": false
}Explain turns any decision into those sentences.
Your ids: user and metadata
Two optional fields on POST /v1/check are stored with the decision and returned with it:
user: your id for the person who wrote the prompt, up to 128 characters. Rules count, tag and pause per user, and you can filter the log by it. If a rule has paused that user, the decision is a block withuser_paused: true, whatever the checks said.metadata: any JSON of your own, up to 4,096 bytes: an order id, a model name, a session.
curl https://api.getvindex.com/v1/check \
-H "Authorization: Bearer $VINDEX_KEY" \
-H "Content-Type: application/json" \
-d '{"input": {"prompt": "a lighthouse at dusk, oil painting"}, "user": "u_42", "metadata": {"order": "1234"}}'The decision log
Every decision is kept, newest first. Filter by outcome, policy, mode, dry_run, check, user, and a time range (since, until), then page with cursor.
curl "https://api.getvindex.com/v1/decisions?outcome=review&dry_run=false&limit=20" \
-H "Authorization: Bearer $VINDEX_KEY"GET /v1/decisions/{id} returns one decision with its input, your metadata and the actions that ran. The full list of fields is in the reference.