BETAWe are currently in beta. Should you encounter any issues, please do not hesitate to contact us.
Discount on all models + if you follow us on twitter and hit us on dm you will get free credit to your email hurry up 🔥🔥🔥 click here
API · ERRORS

Every error tells you what to do next.

Errors carry an error code, a human message, and a next field telling you the recommended remediation. Build your retries and fallbacks against error — never parse message for branching.

The error shape

HTTP 403
{
  "error":      "MODEL_NOT_ALLOWED",
  "message":    "This model isn't enabled for your account.",
  "next":       "Email sales@getvivix.com to request access — we typically respond within 24h.",
  "model":      "video-cinematic-4k",
  "request_id": "req_abc123xyz"
}

Always pass request_id when you write to support — it lets us pull up your exact log line in seconds instead of digging through traces. Every error has one.

Error codes

Auth · 401 / 403

CodeWhenWhat to do
401 UNAUTHORIZEDMissing or malformed Authorization header.Add Authorization: Bearer vvx_live_…. Make sure there's no quote/whitespace around the token.
401 KEY_INVALIDThe key doesn't exist in our records.Generate a new key in Settings → API. The old one was probably revoked or never existed.
401 KEY_REVOKEDThe key was revoked by the user (you, or your admin).Generate a new one. Revocation is permanent.
403 TIER_LOCKEDAPI access requires Pro or Ultimate. Your account is on Free or Standard.Upgrade your plan. Pro starts at $15.
403 MODEL_NOT_ALLOWEDYour account isn't enabled for this specific model.Email sales@getvivix.com to request access. Some models need a per-customer pricing arrangement.

Billing · 402

CodeWhenWhat to do
402 INSUFFICIENT_CREDITSYou don't have enough credits for this generation.Top up at /pricing, wait for your daily drop (free tier), or pick a cheaper config (lower res / shorter duration).

The error body includes balance (what you have) andneeded (what the call would have cost) so you can render a useful upsell to your end-users.

Validation · 422

CodeWhenWhat to do
422 INVALID_PARAMSA required param is missing or out of range.Check the field + reason in the body. Common: prompt over 2000 chars, duration outside the model's range, unknown aspect_ratio.
422 UNKNOWN_MODELThe <code>model</code> slug doesn&apos;t match any in your allowlist.Call GET /api/v1/models to see the exact slugs available to your key.

Rate · 429

CodeWhenWhat to do
429 RATE_LIMITYou&apos;re over your per-minute call limit (Pro 240, Ultimate 600).Back off. Read Retry-After header (in seconds) and retry after that. Long-term: batch where possible or upgrade tier.
429 CONCURRENCY_LIMITToo many in-flight jobs at once (Pro 5, Ultimate 20).Wait for one of your in-flight generations to finish, then retry.

Server · 5xx

CodeWhenWhat to do
500 INTERNAL_ERRORSomething went wrong on our side.Retry with exponential backoff (start at 1s, double each time, max 30s). Capture the request_id for support.
503 PROVIDER_DOWNThe underlying model provider is having an outage.Retry after 60s. We mirror most popular models across providers — falling back may take a few minutes if a major provider is down.

Recommended patterns

Idempotent retries. Generation requests are safe to retry — if the original succeeded, the retry creates a brand-new generation (and you pay twice). If you need true idempotency, dedupe on your side using a request hash before calling.
async function generateWithRetry(payload: any, attempt = 0): Promise<any> {
  const res = await fetch('https://vivix-atelier.vercel.app/api/v1/generate', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.VIVIX_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  })
  if (res.ok) return res.json()

  const body = await res.json()

  // Permanent failures — don't retry.
  if (res.status === 401 || res.status === 402 || res.status === 403 || res.status === 422) {
    throw new ApiError(body)
  }

  // Rate-limited — wait the server's recommended duration.
  if (res.status === 429) {
    const retryAfter = Number(res.headers.get('Retry-After') ?? '5')
    await sleep(retryAfter * 1000)
    return generateWithRetry(payload, attempt + 1)
  }

  // Server-side hiccup — exponential backoff up to 5 attempts.
  if (res.status >= 500 && attempt < 5) {
    await sleep(Math.min(30_000, 1000 * 2 ** attempt))
    return generateWithRetry(payload, attempt + 1)
  }

  throw new ApiError(body)
}

Where to next