Architecture — this panel is a local proxy (default
http://<host>:3080, env PORT). Clients call the panel
endpoints below; the panel performs the generation itself through the single VivaReel
engine (https://vivareel.ai/api) using an account leased from the
vivareel_accounts pool. Vendor names (Kling, Wan, Veo, …) are
cosmetic — see Provider attribution at the bottom.
Key format — VV_Live_<32 hex>, minted server-side
(crypto.randomBytes) by POST /api/vivareel/generate-key (no body).
The full key is returned only in that response; afterwards keys are listed masked
(e.g. VV_Liv…a1b2) and referenced by name
(e.g. vv_1712345678901_a1b2c3).
curl -X POST "http://<host>:3080/api/vivareel/generate-key"
# → { "success": true,
# "key": "VV_Live_<32hex>",
# "name": "vv_1734567890123_a1b2c3", ← use this name (or the full key) as ?apiKey=
# "preview": "VV_Liv…b2c3",
# "keys": [ "panel", "vv_1734567890123_a1b2c3" ] }
Auth — pass apiKey as a query parameter (the panel's own
frontend does exactly this); a body field apiKey or key is also
accepted. Resolution order: SULTAN_API_KEY env override → stored name in
keys.json → raw full key. On /api/vv-generate the resolved key is
read, but the actual upstream auth is the leased pool-account token — the partner key
only matters for the /api/models / /api/balance metadata proxies
(401 = unknown key).
| Endpoint | Method | Returns |
| /api/vv-models | GET | Live model catalog { ok, count, models[] } — the only valid model ids |
| /api/vv-generate | POST | Start a generation → { ok, id, task_id, model, pool{email, credits_before}, json }; poll with id |
| /api/vv-job?id=<job_id> | GET | Job status: in_progress → completed (result + result_url) | failed | poll_error; 404 not_found if unknown/restarted |
| /api/vv-upload-media | POST | Upload a raw reference file (image/video/audio bytes) → { ok, url, public, kind, bytes }; the returned url is fed to /api/vv-generate as a reference |
| /api/vivareel/generate-key | POST | Mint a partner key (above) |
| /api/models?apiKey=… | GET | Metadata proxy → Supabase v1-models (read-only, never generates) |
| /api/balance?apiKey=… | GET | Metadata proxy → Supabase v1-balance (coin balance/limits). No /api/vv-balance exists |
Example — generate video
# 1. Start (model must be an id from /api/vv-models):
curl -X POST "http://<host>:3080/api/vv-generate?apiKey=vv_1734567890123_a1b2c3" \
-H "Content-Type: application/json" \
-d '{
"model": "dashscope-wan3.0-video",
"feature": "video",
"input": {
"prompt": "A cinematic drone shot gliding over a neon city at night",
"aspect_ratio": "16:9",
"duration_seconds": 5,
"resolution": "1080P",
"audio": true
}
}'
# → { "ok": true, "id": "job_…", "task_id": "…", "model": "dashscope-wan3.0-video",
# "pool": { "email": "…", "credits_before": 42 }, "json": { … } }
# 2. Poll until terminal:
curl "http://<host>:3080/api/vv-job?id=job_…"
# → status: queued/in_progress → completed (data.result_url) | failed | timeout | poll_error
Reference uploads — per model
Every model advertises its reference limits in /api/vv-models:
options.max_ref_images and options.max_ref_videos
(absent / 0 = the model takes no references of that kind, and the
panel hides the upload UI). Examples from the live catalog: Gemini Omni Flash 1.1
→ maxRefImages: 5; Seedance 2.0 →
maxRefImages: 4; Gemini Omni Flash 1.1 Free →
maxRefImages: 5, maxRefVideos: 1, maxVideoRefSeconds: 10;
mc-26-pro / mc-3-pro (Motion Control) → exactly 1 image + 1 video.
Sending more than the limit returns 400 too_many_ref_images /
400 too_many_ref_videos with limit and got.
Step 1 — upload the reference file (raw bytes as the request body; the panel
stores it under its static /uploads/ dir and returns a public URL).
Auth is the panel session cookie — the same as every other panel API call.
curl -X POST "http://<host>:3080/api/vv-upload-media?kind=image&name=hero.png" \
-H "Content-Type: image/png" \
--data-binary @hero.png
# → { "ok": true,
# "url": "http://<host>:3080/uploads/image-lx1f-9a3c21.png",
# "public": "…same url…", "kind": "image", "bytes": 184322,
# "content_type": "image/png", "filename": "image-lx1f-9a3c21.png" }
# kind must match the body type: kind=image|video|audio; max 500 MB per file.
# Errors: 400 bad_kind | empty_body | bad_content_type, 413 too_large
Step 2 — pass the reference URL(s) into generation. Reference fields live on
input and are per-model: multiple images go in
reference_image_urls (array, up to max_ref_images);
a single video ref goes in reference_video_url (string) — and
reference_video_urls (array) is also accepted.
# Image-conditioned video (e.g. Gemini Omni Flash 1.1 — 5 image refs):
curl -X POST "http://<host>:3080/api/vv-generate?apiKey=vv_1734567890123_a1b2c3" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-omni-flash-1.1",
"feature": "video",
"input": {
"prompt": "The subject turns and walks into the light",
"reference_image_urls": [
"http://<host>:3080/uploads/image-lx1f-9a3c21.png",
"https://cdn.example.com/second-ref.jpg"
],
"duration_seconds": 10,
"resolution": "720p"
}
}'
# Motion Control (mc-26-pro): exactly 1 image + 1 video reference:
curl -X POST "http://<host>:3080/api/vv-generate?apiKey=vv_1734567890123_a1b2c3" \
-H "Content-Type: application/json" \
-d '{
"model": "mc-26-pro",
"input": {
"prompt": "optional motion instruction, max 2000 chars",
"reference_image_urls": ["http://<host>:3080/uploads/image-….png"],
"reference_video_url": "http://<host>:3080/uploads/video-….mp4"
}
}'
The upstream engine decides image-vs-video routing: image refs are sent
as urls, video refs as reference_video_urls, and
use_reference_image_video is set automatically when both are present.
The panel's VivaReel tab does all of this for you: pick a model, upload files or paste
URLs in the "Reference media" box (shown only when the model accepts refs), and Generate.
Model options (durations, resolutions, refs) are per-model in
/api/vv-models — e.g. Wan 3.0 allows 2–30 s, 480P–1080P, up to 10
reference images. 400 missing_model/unknown_model;
500 pipeline_error (pool lease or upstream). See
ai-model-routing-map.md for the full routing table.
Provider attribution — every model routes
through the single VivaReel engine (vivareel.ai);
the vendor names are cosmetic strings inside the model
field, never separate backends. Current catalog families: Kling, Wan, Qwen,
Veo, Gemini, Seedance, Minimax, Pixverse, GPT, Seedream, Grok, Suno,
Nano Banana — and no Anthropic/Claude models. The live list is
dynamic: always query /api/vv-models (mirrors
_models_live.json), never hardcode ids.