The API supports two authentication methods. Both require aBusinessplan or higher for external calls.
curl -X POST https://viffly.app/api/generate-sync \ -H "X-API-Key: vf_votre_cle_api" \ -F "audio=@narration.mp3" \ -F "title=Ma vidéo"
# Obtenir un token via /api/login
curl -X POST https://viffly.app/api/login \
-H "Content-Type: application/json" \
-d '{"email":"vous@email.com","password":"..."}'
# Réponse : { "token": "eyJhbG..." }
# Utiliser le token
curl -H "Authorization: Bearer eyJhbG..." \
https://viffly.app/api/mevf_...) have a configurable lifetime and are recommended for production.Each AI operation consumes credits (fixed cost for standard features, per-duration for AI video). Free operations (Pexels, Whisper) consume nothing.
/api/credits/costsReturns the credit cost of each operation, the available plans and usage scenarios.
{
"costs": { "render": 5, "ai_script": 1, "ai_voice": 4, "ai_image": 2, "ai_music": 5, "pexels": 0 },
"packages": [
{ "slug": "free", "label": "Free", "monthlyCredits": 15 },
{ "slug": "pro", "label": "Pro", "monthlyCredits": 150 }, ...
],
"scenarios": [ { "name": "Budget", "credits": 5 }, ... ]
}/api/meAuthReturns the account information, including the credit balance.
{
"id": "abc123",
"client_email": "vous@email.com",
"package": "pro",
"creditsTotal": 142,
"creditsMonthly": 130,
"creditsPack": 12,
"monthlyAllowance": 150,
"packageLabel": "Pro",
"creditCosts": { "render": 5, "ai_voice": 4, ... }
}Generates a video fromtext(t2v) or from animage(i2v) via the AI models. This is the API to use from n8n. The flow isasynchronous: you submit a job, then poll its status untildone.
model: "ltx"model: "wan22"X-API-Key./api/ltx/pricingLists the tiers (resolution × duration), aspect ratios and scenarios. No auth required — useful to populate a UI or pick a tier_id.
{
"tiers": [
{ "id": "tier1", "label": "480p · 5s", "resolution": "480p", "seconds": 5, "credits": 5 },
{ "id": "tier2", "label": "720p · 5s", "resolution": "720p", "seconds": 5, "credits": 10 },
{ "id": "tier3", "label": "720p · 10s", "resolution": "720p", "seconds": 10, "credits": 15 },
{ "id": "tier4", "label": "720p · 15s", "resolution": "720p", "seconds": 15, "credits": 22 },
{ "id": "tier5", "label": "720p · 20s", "resolution": "720p", "seconds": 20, "credits": 30 },
{ "id": "tier6", "label": "1080p · 10s","resolution": "1080p", "seconds": 10, "credits": 32 },
{ "id": "tier7", "label": "1080p · 15s","resolution": "1080p", "seconds": 15, "credits": 48 },
{ "id": "tier8", "label": "1080p · 20s","resolution": "1080p", "seconds": 20, "credits": 65 }
],
"aspects": [ { "id": "9:16" }, { "id": "1:1" }, { "id": "16:9" } ],
"scenarios": [ { "id": "cinematic" }, { "id": "product" }, ... ],
"default_negative": "ugly, blurry, low quality, ..."
}/api/ltx/generate10 cr.AuthSubmits a video generation job. Returns a job_id immediately (non-blocking). JSON body.
| Field | Type | Description |
|---|---|---|
| mode* | string | "t2v" (text→video) or "i2v" (image→video) |
| prompt* | string | Video description (≥ 3 characters) |
| tier_id | string | tier1…tier8 (resolution × duration). Default: tier2 |
| model | string | "ltx" (default, fast) or "wan22" (cinematic, ×2 cr.) |
| image_base64 | string | Starting image in base64 (required if mode=i2v). Without the data: prefix |
| image_url | string | Alternative: public HTTPS URL of the starting image (i2v) |
| negative | string | Negative prompt (default: see /api/ltx/pricing) |
| aspect | string | "9:16" (default), "1:1", "16:9" |
| scenario_type | string | Creative direction (see /api/ltx/pricing) |
| fps | number | Frames/second (default: 24) |
| seed | number | Seed (reproducibility). Random if omitted |
| audio_on | boolean | Keep the generated audio track (default: true) |
{
"job_id": "915c93ea-58b7-4808-a83f-5dfa98ee1ef5",
"status": "processing",
"tier": { "id": "tier2", "label": "720p · 5s", "credits": 10 }
}curl -X POST https://viffly.app/api/ltx/generate \
-H "X-API-Key: vf_votre_cle" \
-H "Content-Type: application/json" \
-d '{
"mode": "t2v",
"model": "ltx",
"prompt": "a sports car drifting on a coastal road at sunset, cinematic",
"tier_id": "tier2",
"aspect": "9:16"
}'curl -X POST https://viffly.app/api/ltx/generate \
-H "X-API-Key: vf_votre_cle" \
-H "Content-Type: application/json" \
-d '{
"mode": "i2v",
"model": "wan22",
"prompt": "slow cinematic push in, gentle motion",
"tier_id": "tier2",
"image_base64": "'"$(base64 -w0 photo.jpg)"'"
}'/api/ltx/generate-sync10 cr.AuthSYNCHRONOUS version: submits, waits for the render internally, then returns the video — in ONE single call (no wait/poll loop). Ideal for a single n8n node. Set a long timeout (e.g. 1200000 ms).
| Field | Type | Description |
|---|---|---|
| (same as /api/ltx/generate)* | json | mode, prompt, tier_id, model, image_base64/image_url, aspect… |
// Par défaut → renvoie le FICHIER MP4 (binaire). Dans n8n : Response Format = File.
// Avec ?format=json → renvoie l'URL à la place :
{
"status": "done",
"job_id": "915c93ea-...",
"output_url": "/videos/ltx_915c93ea-....mp4",
"url": "https://viffly.app/videos/ltx_915c93ea-....mp4"
}# Récupère directement le MP4 (sortie binaire)
curl -X POST https://viffly.app/api/ltx/generate-sync \
-H "X-API-Key: vf_votre_cle" -H "Content-Type: application/json" \
-d '{"mode":"t2v","model":"ltx","prompt":"...","tier_id":"tier2"}' \
--max-time 1200 -o video.mp4
# Ou récupère juste l'URL (JSON)
curl -X POST "https://viffly.app/api/ltx/generate-sync?format=json" \
-H "X-API-Key: vf_votre_cle" -H "Content-Type: application/json" \
-d '{"mode":"t2v","prompt":"...","tier_id":"tier2"}'/api/ltx/status/:idAuth(Async mode) Polls the state of a job. Call it in a loop (every ~5-10 s) until status = done or error. Not needed if you use /generate-sync.
// en cours
{ "id": "915c93ea-...", "status": "processing", "progress": 50 }
// terminé
{
"id": "915c93ea-...",
"status": "done",
"output_url": "/videos/ltx_915c93ea-....mp4", // relatif → préfixer https://viffly.app
"progress": 100
}
// échec (crédits remboursés)
{ "id": "915c93ea-...", "status": "error", "error": "..." }output_urlisrelative. Full download URL =https://viffly.app+output_url.The video flow is asynchronous. In n8n, the pattern is:Submit→Wait→Check the status→ loop while ≠done.
Configure the options below then generate the HTTP Request node JSON — copy it and paste it directly into the n8n canvas (Ctrl+V), or download the file.
| Method | POST |
| URL | https://viffly.app/api/ltx/generate |
| Authentication | None (we pass the key in the header below) |
| Send Headers | ON → Name: X-API-Key | Value: vf_votre_cle |
| Send Body | ON → Body Content Type: JSON |
| Specify Body | Using JSON |
{
"mode": "i2v",
"model": "ltx",
"prompt": "{{ $json.prompt }}",
"tier_id": "tier2",
"aspect": "9:16",
"image_base64": "{{ $binary.data.data }}"
}i2v: if the image comes from a previous node (e.g. Telegram, Read Binary File), reference its base64 binary data with{{ $binary.data.data }}. For t2v, removeimage_base64and set"mode": "t2v".
Node 2 — Wait: 10 seconds.Node 3 — HTTP Request(poll):
| Method | GET |
| URL | https://viffly.app/api/ltx/status/{{ $('NODE 1').item.json.job_id }} |
| Send Headers | ON → X-API-Key: vf_votre_cle |
NodeIF— condition (String):{{ $json.status }} equals done.
https://viffly.app{{ $json.output_url }}| Method | GET |
| URL | https://viffly.app{{ $json.output_url }} |
| Response Format | File / Binary |
vf_…("API" tab) is enough./api/generate-sync5 cr.AuthGenerates a video and waits for the result (20 min timeout). Returns the download URL.
| Field | Type | Description |
|---|---|---|
| audio* | file | MP3 audio file of the narration |
| images | file[] | Images (1 to 4 files). Alternative: image_url |
| image_url | string | URL of an image (alternative to images) |
| image_url_0..N | string | Indexed image URLs, no longer capped at 3 (up to 30 media). An .mp4/.mov URL here plays as a video clip at that position. |
| media | string[] | Ordered JSON array mixing images AND videos — array order = display order. Videos detected by extension (.mp4, .mov, .webm…). |
| format | string | Output format: 9:16 (default), 16:9, 1:1, 4:5, 4:3 |
| title | string | Title shown in the intro |
| source | string | Source / site name |
| script_text | string | Script text for the subtitles |
| template | string | 32 templates : default, bold, clean, news, minimal, highlight, cinematic, neon, retro, gaming, story, meme, comic, glitch, noir, vaporwave, duotone, matrix, ticker… |
| caption_style | string | karaoke, glow, outline, neon, block, shadow, gradient, typewriter |
| caption_size | string | S, M, L, XL |
| caption_position | string | top, center, bottom |
| language | string | fr, en, ar, es, de, it, pt |
| profile | string | Render profile name (e.g. shadesplays) |
| music_url | string | Background music URL (loops automatically when the video outlasts the track) |
| music_start_at | number | Music start offset (seconds) |
| voice_type | string | "ai" for ElevenLabs narration (+4 cr.) |
| voice_id | string | ElevenLabs voice ID |
| outro_text | string | End screen text |
| outro_sub | string | End screen subtitle |
{
"videoId": "7865aac1-c301-4060-bd6e-6ca4fe3f6859",
"url": "https://s3.eu-west-3.amazonaws.com/.../render.mp4",
"thumbnail": "https://s3.eu-west-3.amazonaws.com/.../thumb.jpg",
"duration": 27.5,
"size": 8542190
}curl -X POST https://viffly.app/api/generate-sync \ -H "X-API-Key: vf_votre_cle" \ -F "audio=@narration.mp3" \ -F "format=16:9" \ -F 'media=["https://cdn.exemple.com/clip.mp4","https://cdn.exemple.com/photo1.jpg","https://cdn.exemple.com/photo2.jpg"]' \ -F "title=Mon titre" \ -F "source=MonSite.com" \ -F "template=gaming" \ -F "caption_style=karaoke" \ -F "caption_size=L" \ -F "script_text=Voici le texte des sous-titres" \ -F "profile=shadesplays"
/api/generate5 cr.AuthStarts the generation in the background. Returns a jobId to track progress via SSE.
{ "jobId": "e9e2d4f9-..." }GET /api/videos/events?jobId=...(Server-Sent Events)/api/videosAuthLists all the account's videos, sorted by descending date.
[
{
"id": "7865aac1-...",
"title": "Mon titre",
"status": "done",
"url": "https://...",
"thumbnail": "https://...",
"size": 8542190,
"duration": 27.5,
"created": "2026-03-28T..."
}, ...
]/api/videos/:id.mp4AuthDownload a video's MP4 file.
/api/videos/:idAuthDelete a video and its associated files.
/api/ai/script1 cr.AuthGenerates a narration script optimized for virality.
| Field | Type | Description |
|---|---|---|
| prompt* | string | Topic or instruction |
| existing | string | Existing script to improve |
| context | string | Additional context (web content) |
| language | string | Target language (fr, en, ar...) |
{ "script": "Saviez-vous que...", "charsUsed": 342, "charsMax": 700 }/api/generate-images2 cr.AuthGenerates images via FAL Flux 2 Pro.
| Field | Type | Description |
|---|---|---|
| prompt* | string | Image description |
| count | number | Number of images (1-3, default: 3) |
{ "images": ["https://fal.media/...", ...] }/api/ai/image-promptAuthGenerates 3 image prompts optimized for a given topic.
| Field | Type | Description |
|---|---|---|
| topic* | string | Video topic |
| style | string | Desired visual style |
{ "prompts": ["A cinematic...", "An aerial...", "A close-up..."] }/api/generate-music5 cr.AuthGenerates background music via Suno AI.
| Field | Type | Description |
|---|---|---|
| prompt | string | Description of the musical style |
| style | string | Music genre (pop, electronic, cinematic...) |
| instrumental | boolean | Instrumental only (default: true) |
| title | string | Track title |
{ "taskId": "abc123", "status": "pending" }GET /api/generate-music/:taskId/api/elevenlabs/voicesAuthLists the available ElevenLabs voices (1h cache).
{ "voices": [{ "voice_id": "pNInz6ob...", "name": "Adam", "labels": {...} }, ...] }/api/keysAuthLists all your API keys.
[{
"id": "uuid", "name": "Production", "key_prefix": "vf_a1b2c3d4",
"scopes": ["generate","videos"], "rate_limit": 60,
"expires_at": null, "last_used_at": "2026-03-28T...",
"usage_count": 42, "is_active": true
}]/api/keysAuthCreates a new API key. The full key is only returned at creation.
| Field | Type | Description |
|---|---|---|
| name | string | Key name (e.g. Production) |
| scopes | string[] | Permissions: generate, videos, credits, ai_script, ai_images, ai_music |
| rate_limit | number | Requests per minute (1-1000, default: 60) |
| expires_in_days | number | Days before expiration (null = never) |
{
"id": "uuid",
"key": "vf_a1b2c3d4e5f6...", // ⚠️ Affiché UNE SEULE FOIS
"name": "Production",
"scopes": ["generate", "videos", "credits"]
}/api/keys/:idAuthEdit a key (name, permissions, rate limit, enable/disable).
| Field | Type | Description |
|---|---|---|
| name | string | New name |
| is_active | boolean | Enable/disable |
| scopes | string[] | New permissions |
| rate_limit | number | New rate limit |
/api/keys/:idAuthPermanently deletes an API key.
/api/musicAuthLists the music library (uploaded tracks + Suno).
/api/pexels/imagesAuthPexels image search.
| Field | Type | Description |
|---|---|---|
| q | query | Search term |
| page | query | Page (default: 1) |
| per_page | query | Results per page (default: 20) |
/api/pexels/videosAuthPexels video search.
| Code | Meaning | Action |
|---|---|---|
| 401 | Invalid or expired token | Renew the token or create a new API key |
| 402 | Insufficient credits | Buy a pack or upgrade to a higher plan |
| 403 | Insufficient plan (Business+ required) | Upgrade to Business or Agency |
| 429 | Rate limit exceeded | Wait 1 minute or increase the rate limit |
| 500 | Server error | Retry or contact support |
{
"error": "Message descriptif de l'erreur",
"creditsNeeded": 5 // (optionnel) crédits nécessaires
}The default limits are: