VindexDocs

Concepts

Idempotency

Networks drop requests. Send an Idempotency-Key with each check and you can retry as often as you like: the prompt is screened once, logged once and counted once.

How it works

Put a key of your own on POST /v1/check, one per prompt. Your order id or generation id is ideal.

Shell
curl -i https://api.getvindex.com/v1/check \
  -H "Authorization: Bearer $VINDEX_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1234" \
  -d '{"input": {"prompt": "a lighthouse at dusk, oil painting"}}'
  • The first request with a key screens the prompt and logs the decision.
  • A repeat with the same key and the same body returns that first decision, unchanged, with the header Idempotent-Replayed: true. No second screening, log entry, review item or webhook.
  • Two at once with the same key make one decision; both get it.
  • The same key with a different body gets 422 idempotency_key_reused. The dry-run header counts as part of the body.
422 idempotency_key_reused
{
  "type": "https://api.getvindex.com/problems/idempotency_key_reused",
  "title": "Idempotency key reused",
  "status": 422,
  "detail": "This Idempotency-Key was already used with a different request body.",
  "code": "idempotency_key_reused"
}

Keys are 1 to 255 printable ASCII characters, scoped to your workspace, and remembered as long as the decision they made.

When to retry

Retry on 429, 500, 503, or when no response came back at all. Wait for Retry-After when there is one. Don't retry other 4xx errors: the same request will fail the same way.

// retry.mjs · Node 18+ · VINDEX_KEY=vx_test_... node retry.mjs
import { randomUUID } from "node:crypto";

async function check(prompt, idempotencyKey = randomUUID()) {
  for (let attempt = 1; ; attempt++) {
    let res;
    try {
      res = await fetch("https://api.getvindex.com/v1/check", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.VINDEX_KEY}`,
          "Content-Type": "application/json",
          "Idempotency-Key": idempotencyKey,
        },
        body: JSON.stringify({ input: { prompt } }),
        signal: AbortSignal.timeout(10_000),
      });
    } catch (err) {
      if (attempt >= 4) throw err; // no response at all: safe to retry with the same key
      await new Promise((r) => setTimeout(r, attempt * 1000));
      continue;
    }
    if (res.ok) return res.json();

    const retryable = [429, 500, 503].includes(res.status);
    if (!retryable || attempt >= 4) {
      const problem = await res.json();
      throw new Error(`${res.status} ${problem.code}: ${problem.detail ?? problem.title}`);
    }
    const wait = Number(res.headers.get("retry-after") ?? attempt);
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
}

const decision = await check("a lighthouse at dusk, oil painting", "order-1234");
console.log(decision.outcome);