# Unified Media Studio API for n8n This reference describes every business API endpoint exposed by the unified Space and how to call it with n8n's **HTTP Request** node. ## Base URL Store the deployed Space URL in an n8n variable: ```text STUDIO_BASE_URL=https://.hf.space ``` Use this expression in HTTP Request nodes: ```text {{ $vars.STUDIO_BASE_URL }}/health ``` Do not add a trailing slash to `STUDIO_BASE_URL`. ## Standard HTTP Request Node Settings ### JSON request - **Method:** Use the method in the endpoint tables. - **URL:** `{{ $vars.STUDIO_BASE_URL }}` - **Send Body:** On - **Body Content Type:** JSON - **Specify Body:** Using JSON - **Response Format:** JSON For routes marked **API key**, add a Header Auth credential or enable **Send Headers**: ```text X-API-Key: ``` The header is only enforced when `AVA2LON_API_KEY` or `BASYX_API_KEY` is set in the Space environment. ### Query parameters - Enable **Send Query Parameters**. - Add each query parameter shown in the endpoint table. - Do not place simple query parameters in the JSON body. ### Multipart file upload - **Send Body:** On - **Body Content Type:** Form-Data - For text fields, select **Parameter Type: Form Data**. - For files, select **Parameter Type: n8n Binary File**. - Set **Name** to the API field name, usually `file` or `files`. - Set **Input Data Field Name** to the incoming n8n binary field, commonly `data`. Add another `files` parameter for every additional binary file. ### File download response For endpoints marked **File response**: - Set **Response Format** to File. - Set **Put Output in Field** to `data`. - The following n8n node can read the output from `$binary.data`. ### Asynchronous job workflow Most renderer operations return: ```json { "job_id": "job_123", "status_url": "/status/job_123", "download_url": "/download/job_123?token=..." } ``` Recommended n8n flow: 1. HTTP Request submits the job. 2. Wait node pauses for several seconds. 3. HTTP Request calls `{{ $vars.STUDIO_BASE_URL }}{{ $json.status_url }}`. 4. IF node checks whether `state` equals `COMPLETED`, `FAILED`, or `CANCELLED`. 5. Loop through Wait while the state is `PENDING` or `RUNNING`. 6. Download using the original submission node's `download_url`. For payloads with `callback_url`, use an n8n Webhook node's production URL to avoid polling. Render callbacks use `render.completed`, `render.failed`, and `render.cancelled`; toolkit and analysis callbacks use `task.*`. The event is available in both the JSON `event` property and `X-Ava2lon-Event` header. ## Root and Discovery | Method | Path | n8n input | Response | | --- | --- | --- | --- | | GET | `/` | None | Redirects to `/dashboard`; use only for browser access. | | GET | `/apps` | None | JSON map of mounted applications and MCP tool count. | ## Renderer Discovery and Monitoring These routes use the root base URL. | Method | Path | n8n input | Response | | --- | --- | --- | --- | | GET | `/health` | None | Renderer health. | | GET | `/healthz` | None | Health-check alias for hosting probes. | | GET | `/monitor` | None | Runtime, queue, storage, and capability summary. | | GET | `/queue` | None | Job queue summary. | | GET | `/workers` | None | Worker and runtime settings. | | GET | `/presets` | None | Presets, templates, styles, transitions, and task IDs. | | GET | `/platforms` | None | Platform profile metadata. | | GET | `/toolkit/tasks` | None | Renderer toolkit task names. | | GET | `/capabilities` | None | Complete capability catalog. | | GET | `/effects` | None | Effects catalog. | | GET | `/filters` | None | Filter catalog. | | GET | `/transitions` | None | Transition catalog. | | GET | `/templates/catalog` | None | Templates, presets, and styles. | | GET | `/projects` | None | List saved projects. | ## Projects and Timeline | Method | Path | n8n input | Auth | Response | | --- | --- | --- | --- | --- | | POST | `/projects` | JSON `ProjectPayload` | API key | Created project. | | POST | `/project/create` | JSON `ProjectPayload` | API key | Alias of `/projects`. | | GET | `/project/{project_id}` | Replace `{project_id}` | None | Project JSON. | | POST | `/project/save` | JSON `ProjectSavePayload` | API key | Saved project. | | POST | `/project/assets/add` | JSON `ProjectAssetPayload` | API key | Updated project. | | POST | `/timeline/add` | JSON `TimelineAddPayload` | API key | Updated project. | | POST | `/timeline/operation` | JSON `TimelineOperationPayload` | API key | Updated project. | | POST | `/timeline/split` | JSON `TimelineOperationPayload` | API key | Runs `split`. | | POST | `/timeline/trim` | JSON `TimelineOperationPayload` | API key | Runs `trim`. | | POST | `/timeline/ripple-delete` | JSON `TimelineOperationPayload` | API key | Runs `ripple_delete`. | | POST | `/timeline/insert` | JSON `TimelineOperationPayload` | API key | Runs `insert`. | | POST | `/timeline/replace` | JSON `TimelineOperationPayload` | API key | Runs `replace`. | | POST | `/timeline/group` | JSON `TimelineOperationPayload` | API key | Runs `group`. | | POST | `/timeline/lock` | JSON `TimelineOperationPayload` | API key | Runs `lock`. | | POST | `/timeline/hide` | JSON `TimelineOperationPayload` | API key | Runs `hide`. | | POST | `/timeline/duplicate` | JSON `TimelineOperationPayload` | API key | Runs `duplicate`. | | POST | `/effect/apply` | JSON `EffectApplyPayload` | API key | Updated project or asynchronous job. | | POST | `/filter/apply` | JSON `FilterApplyPayload` | API key | Updated project or asynchronous job. | | POST | `/transition/add` | JSON `TransitionAddPayload` | API key | Updated project or asynchronous job. | | POST | `/keyframe/add` | JSON `KeyframePayload` | API key | Updated project. | Example project creation: ```json { "name": "Campaign 01", "metadata": { "platform": "tiktok", "creative_style": "high_energy" }, "export_settings": { "platform": "tiktok" } } ``` ## AI and Generation All routes in this table return asynchronous job information. | Method | Path | n8n input | Auth | | --- | --- | --- | --- | | POST | `/caption/generate` | JSON `CaptionGeneratePayload` | API key | | POST | `/music/generate` | JSON `GenerationPayload` | API key | | POST | `/voice/generate` | JSON `GenerationPayload` | API key | | POST | `/image/generate` | JSON `GenerationPayload` | API key | | POST | `/video/generate` | JSON `GenerationPayload` | API key | | POST | `/ai/{tool}` | Replace `{tool}`; JSON `AIToolPayload` | API key | | POST | `/assistant/{tool}` | Replace `{tool}`; JSON `AIToolPayload` | API key | Example: ```json { "prompt": "Upbeat cinematic background music", "provider": "local", "callback_url": "https://n8n.example/webhook/studio-complete", "params": { "duration": 20 } } ``` ## Rendering and Uploads | Method | Path | n8n input | Auth | Response | | --- | --- | --- | --- | --- | | POST | `/render` | JSON `RenderPayload` or `AIReelsPayload` | None | Async job. | | POST | `/render/variants` | JSON `RenderVariantsPayload` | None | Render group plus child jobs. | | POST | `/render/template` | JSON `TemplateRenderPayload` | None | Async job. | | POST | `/render/ai-reels` | JSON `AIReelsPayload` | None | Async job. | | POST | `/render/batch` | JSON `BatchPayload` | None | Array of job IDs. | | POST | `/automation/batch` | JSON `BatchPayload` | API key | Array of job IDs. | | POST | `/project/{project_id}/render` | Replace `{project_id}`; JSON `ProjectRenderPayload` | API key | Async job. | | POST | `/render/upload` | Multipart `request_json` plus zero or more `files` | None | Async job. | | POST | `/render/ai-reels/upload` | Multipart `request_json` plus zero or more `files` | None | Async job. | | POST | `/assets/upload` | Multipart; one or more `files` | None | Uploaded asset references. | | POST | `/upload` | Multipart `files`; optional `expand_zip` | None | Uploaded or expanded assets. | | GET | `/render/groups/{group_id}` | Replace `{group_id}` | None | Aggregate state and child jobs. | | GET | `/templates/automation` | None | None | Data-driven render templates. | Basic render body: ```json { "scenes": [ { "start": 0, "duration": 5, "media": "https://example.com/video.mp4", "caption": "Opening hook", "transition": "fade", "background": "blur", "layout": "fill" } ], "output_name": "campaign.mp4", "template": "tiktok_classic", "auto_subtitles": true, "subtitle_language": "en" } ``` For `/render/upload`, configure n8n Form-Data as: | Parameter Type | Name | Value | | --- | --- | --- | | Form Data | `request_json` | JSON string shown below | | n8n Binary File | `files` | Input Data Field Name such as `data` | ```json { "scenes": [ { "start": 0, "duration": 5, "media": "upload://clip.mp4", "caption": "Uploaded clip" } ], "output_name": "render.mp4" } ``` The filename in `upload://clip.mp4` must match the uploaded binary filename. Render several platform outputs from one timeline with `/render/variants`: ```json { "scenes": [{"start": 0, "duration": 12, "media": "https://example.com/input.mp4"}], "callback_url": "https://n8n.example/webhook/render-events", "variants": [ {"name": "shorts", "platform": "youtube_shorts", "output_name": "shorts.mp4"}, {"name": "reels", "platform": "instagram_reels", "output_name": "reels.mp4"}, {"name": "youtube", "platform": "youtube_1080p", "output_name": "youtube.mp4"} ] } ``` Use `/render/template` for spreadsheet, CRM, and database-driven renders. An exact placeholder preserves arrays, objects, numbers, and booleans instead of converting them to strings. ```json { "template": "vertical-captioned-clip", "variables": { "output_name": "customer-42.mp4", "scenes": [{"start": 0, "duration": 8, "media": "https://example.com/42.mp4"}], "voiceover": null, "background_music": null } } ``` ## Platform Processing and Toolkit All routes in this table require `X-API-Key` and return asynchronous job information. | Method | Path | n8n input | | --- | --- | --- | | POST | `/ingest` | JSON `IngestPayload` | | POST | `/analyze` | JSON `AnalyzePayload` | | POST | `/clips` | JSON `ClipsPayload` | | POST | `/thumbnail` | JSON `ThumbnailPayload` | | POST | `/thumbnail/create` | JSON `ThumbnailPayload` | | POST | `/metadata` | JSON `MetadataPayload` | | POST | `/publish` | JSON `PublishPayload` | | POST | `/toolkit` | JSON `ToolkitPayload` | | POST | `/edit` | JSON `ToolkitPayload`; alias of `/toolkit` | Toolkit example: ```json { "task": "compress", "media": "https://example.com/input.mp4", "output_name": "compressed.mp4", "params": { "crf": 28, "resolution": "720p" } } ``` FFmpeg automation tasks added for n8n: | Task | Important `params` | Download artifact | | --- | --- | --- | | `inspect` | None | FFprobe metadata JSON | | `loudness_analyze` | `target_lufs` | LUFS/LRA/true-peak JSON | | `silence_detect` | `noise_db`, `duration_seconds` | Timed silence events JSON | | `black_detect` | `pixel_threshold`, `duration_seconds` | Timed black-frame events JSON | | `scene_detect` | `threshold` | Scene-change timestamps JSON | | `contact_sheet` | `columns`, `rows`, `interval_seconds`, `thumbnail_width` | JPEG/PNG | | `hls` | `segment_seconds`, `playlist_type`, `crf` | ZIP with M3U8 and TS segments | All use the normal `POST /toolkit` body with `task`, `media`, optional `params`, and optional `callback_url`. ## Transcription, Subtitles, Jobs, and Downloads | Method | Path | n8n input | Response | | --- | --- | --- | --- | | POST | `/transcribe` | JSON `TranscribePayload` | Transcription JSON. | | POST | `/subtitles` | JSON `SubtitlePayload` | File response (`.srt` or `.ass`). | | POST | `/scene-builder` | JSON `SceneBuildPayload` | Generated scene array. | | POST | `/transcribe/upload` | Multipart `file` plus optional transcription fields | Transcription JSON. | | GET | `/status` | Optional query `job_id` | Service status without `job_id`; job record with it. | | GET | `/status/{job_id}` | Replace `{job_id}` | Job record. | | POST | `/cancel/{job_id}` | Replace `{job_id}` | Updated job record. | | POST | `/admin/cleanup` | Optional query `older_than_seconds` | Cleanup counts. | | GET | `/download` | Query `job_id`; optional `token` | File response. | | GET | `/download/{job_id}` | Replace `{job_id}`; optional query `token` | File response. | | POST | `/inspect` | Query `path` | Media metadata JSON. | | POST | `/media/inspect` | JSON `{ "media": "...", "callback_url": "..." }` | Async remote-safe inspection. | Transcription body: ```json { "audio": "https://example.com/audio.mp3", "model_size": "tiny", "language": "en", "task": "transcribe", "beam_size": 5, "vad_filter": true, "word_timestamps": true } ``` Subtitle body: ```json { "events": [ { "start": 0, "end": 2.5, "text": "Opening caption" } ], "format": "srt", "template": "tiktok_classic" } ``` ## Kokoro TTS | Method | Path | n8n input | Response | | --- | --- | --- | --- | | POST | `/tts/v1/audio/speech` | JSON shown below | WAV file response. | Set the HTTP Request node's response format to **File**. ```json { "input": "This is generated speech.", "voice": "af_bella.pt", "model": "kokoro-v0_19.onnx", "speed": 1.0 } ``` `voice` and `model` are optional; the server uses its first configured voice and model when omitted. ## MusicGen | Method | Path | n8n input | Response | | --- | --- | --- | --- | | GET | `/music/health` | None | Health JSON. | | POST | `/music/generate` | Query `prompt`; optional query `duration` | WAV file response. | For generation: - **Method:** POST - **URL:** `{{ $vars.STUDIO_BASE_URL }}/music/generate` - **Send Query Parameters:** On - `prompt`: `Soft cinematic instrumental` - `duration`: `10` - **Response Format:** File ## Whisper Operator | Method | Path | n8n input | Response | | --- | --- | --- | --- | | POST | `/whisper/execute/{task_name}` | Multipart file or URL fields | JSON or file response. | | GET | `/whisper/api/health` | None | Health JSON. | | GET | `/whisper/api/status/{job_id}` | Replace `{job_id}` | Whisper queue status. | Use one of these multipart inputs for `/whisper/execute/{task_name}`: - `file`: n8n Binary File. - `url_input`: downloadable media URL. - `source`: alternative media URL. - `webhook`: optional callback URL. Supported task names: ```text autonomous auto-publish publish bulk-publish generate-metadata generate-thumbnail schedule-post transcribe subtitles render highlights clips viral-score strategy batch ``` ## FFmpeg Editor | Method | Path | n8n input | Response | | --- | --- | --- | --- | | GET | `/editor/healthz` | None | Health and dependency checks. | | GET | `/editor/readyz` | None | Readiness; returns 503 when dependencies fail. | | GET | `/editor/tasks` | None | Editor task registry. | | POST | `/editor/n8n/execute/{task_id}` | JSON, multipart, or raw binary | Synchronous file response. | | POST | `/editor/n8n/jobs/{task_id}` | JSON, multipart, or raw binary | Background job JSON. | | POST | `/editor/execute/{task_id}` | Multipart fields | Synchronous file response. | | POST | `/editor/jobs/{task_id}` | Multipart fields | Background job JSON. | | GET | `/editor/status/{job_id}` | Replace `{job_id}` | Background job status. | | GET | `/editor/download/{job_id}` | Replace `{job_id}` | File response. | | GET | `/editor/history` | None | Recent output metadata. | | GET | `/editor/history/{history_id}/download` | Replace `{history_id}` | File response. | ### Recommended editor request for n8n Use `/editor/n8n/jobs/{task_id}` for long-running jobs. Multipart request: - Add one or more **n8n Binary File** parameters. The field name may be `files`. - Add text options as **Form Data**. - A URL may use `url`, `urls`, `file_url`, `source_url`, or `download_url`. JSON request: ```json { "url": "https://example.com/input.mp4", "options": { "resolution": "720p", "crf": "28", "preset": "medium", "text": "Caption text" } } ``` Raw binary request: - **Body Content Type:** n8n Binary File - **Input Data Field Name:** `data` - Add header `X-Filename: input.mp4` - Put editor options in query parameters. Available editor option fields: ```text text start_time end_time duration aspect_ratio resolution crf preset audio_bitrate volume position opacity fps width speed timestamp image_duration frame_rate font_size wave_color ``` ### Editor task IDs Use one value below as `{task_id}`: | Task ID | Purpose | Files | | --- | --- | --- | | `normalize` | Convert to H.264/AAC MP4 | 1 video | | `extract_audio` | Export MP3 audio | 1 video/audio | | `resize_916` | Crop to vertical 9:16 | 1 video | | `add_subtitles` | Burn SRT/ASS subtitles | video + subtitle | | `burn_lyrics` | Burn styled lyrics | video + subtitle | | `text_overlay` | Add caption text | 1 video | | `merge_music` | Add background music | video + audio | | `thumbnail` | Export JPG frame | 1 video | | `watermark` | Overlay logo/image | video + image | | `compress` | Reduce video size | 1 video | | `batch_compress` | Compress videos into ZIP | 1+ videos | | `make_gif` | Create GIF | 1 video | | `tiktok_lyrics` | Vertical lyric clip | 1 video | | `tiktok_pro_reframer` | Vertical reframe and style | 1 video | | `reels_blur_fit` | Blurred 9:16 fit | 1 video | | `reels_safe_caption` | Mobile-safe caption | 1 video | | `reels_hook_title` | Top hook/title | 1 video | | `reels_progress_bar` | Progress bar and trim | 1 video | | `reels_loop` | Loop to target duration | 1 video | | `reels_subtitle_safe` | Mobile-safe subtitles | video + subtitle | | `reels_reaction_stack` | Stack reaction videos | 2 videos | | `reels_audio_duck` | Duck music below video | video + audio | | `faceless_quote_card` | Text-only quote short | no file | | `faceless_story_pages` | Timed text story slides | no file | | `faceless_image_narration` | Image with narration | image + audio | | `faceless_video_narration` | B-roll with narration | video + audio | | `faceless_broll_montage` | Silent b-roll montage | 2+ videos | | `series_split_pack` | Split long video into series ZIP | 1 video | | `series_episode_badge` | Add episode badge | 1 video | | `series_batch_pack` | Package clips as numbered series | 2+ videos | | `series_recap_card` | Text-only recap card | no file | | `concat` | Join clips | 2+ videos | | `slideshow` | Build video from images | 2+ images | | `trim` | Cut by start/end | 1 video/audio | | `crop_aspect` | Crop to target aspect ratio | 1 video | | `waveform` | Create waveform video | 1 audio/video | | `extract_frames` | Export frames as ZIP | 1 video | | `add_intro_outro` | Join intro/main/outro | 2-3 videos | | `speed` | Change playback speed | 1 video | | `remove_audio` | Remove video audio | 1 video | | `replace_audio` | Replace video audio | video + audio | ## JSON Payload Reference Fields marked `*` are required. ### Rendering `ScenePayload` ```text start*: number >= 0 duration*: number > 0 media*: string path or URL caption: string transition: string = "fade" background: string = "blur" layout: string = "fill" effect: string | null ``` `RenderPayload` ```text scenes*: ScenePayload[] template: string = "tiktok_classic" preset, creative_style, platform: string | null output_name: string = "render.mp4" voiceover, background_music: string | null music_volume: number = 0.316 music_fade_in, music_fade_out, music_start: number = 0 music_loop, music_ducking: boolean = true voice_volume: number = 1 subtitle_format: string = "ass" auto_subtitles: boolean = false subtitle_language, whisper_model_size: string | null preview, audio_normalize: boolean = false watermark, intro, outro: string | null watermark_position: string = "bottom-right" callback_url, export_target: string | null priority: integer = 0 scheduled_at: number | null normalize: boolean = true metadata: object = {} ``` `AIReelsPayload` ```text script*: string voiceover*: string assets*: string[] template: string = "tiktok_classic" creative_style, platform: string | null output_name: string = "ai_reel.mp4" background_music: string | null music_volume: number = 0.316 music_fade_in, music_fade_out, music_start: number = 0 music_loop, music_ducking: boolean = true voice_volume: number = 1 callback_url, export_target: string | null ``` `BatchPayload` ```text jobs*: RenderPayload[] ``` `ProjectRenderPayload` ```text output_name: string = "project_render.mp4" template, preset, creative_style, platform: string | null callback_url, export_target: string | null preview: boolean = false normalize: boolean = true metadata: object = {} ``` ### Projects and Editing `ProjectPayload` ```text name*: string metadata: object = {} export_settings: object = {} template: object | null ``` `ProjectSavePayload` ```text project_id: string | null project*: object ``` `ProjectAssetPayload` ```text project_id*: string asset*: object ``` `TimelineAddPayload` ```text project_id*: string track_type: string = "video" track_id: string | null item: object = {} ``` `TimelineOperationPayload` ```text project_id*: string operation: string = "drag" item_id: string | null params: object = {} ``` `EffectApplyPayload` ```text project_id, target_id, item_id: string | null effect*: string params: object = {} callback_url: string | null ``` `FilterApplyPayload` ```text project_id, target_id, item_id: string | null filter*: string params: object = {} lut: string | null callback_url: string | null ``` `TransitionAddPayload` ```text project_id, from_item_id, to_item_id, target_id: string | null transition*: string duration: number = 0.45 params: object = {} callback_url: string | null ``` `KeyframePayload` ```text project_id*: string target_id*: string property*: string time*: number >= 0 value*: any JSON value easing: string = "linear" ``` ### Generation and Platform Tools `GenerationPayload` ```text prompt, text, media, provider: string | null callback_url, export_target: string | null params: object = {} additional JSON fields are accepted ``` `CaptionGeneratePayload` ```text media, audio, text, transcript: string | null events: object[] | null template: string = "capcut" language: string | null engine: string = "whisper" word_timestamps: boolean = true emoji_insertion, speaker_detection: boolean = false karaoke, animated: boolean = true callback_url: string | null additional JSON fields are accepted ``` `AIToolPayload` ```text project_id, media, transcript, text, platform: string | null callback_url: string | null params: object = {} additional JSON fields are accepted ``` `IngestPayload` ```text sources*: [{ "url": string, "type": string|null, "name": string|null }] callback_url: string | null ``` `AnalyzePayload` ```text media*: string transcript: string = "" platform, callback_url: string | null ``` `ClipsPayload` ```text media*: string clips: object[] | null callback_url: string | null ``` `ThumbnailPayload` ```text media*: string text: string = "" timestamp: number | null template: string = "bold" callback_url: string | null ``` `MetadataPayload` ```text topic, transcript: string = "" platform, callback_url: string | null ``` `PublishPayload` ```text media, asset, title, description: string | null platforms: string[] = [] platform, scheduled_at: string | null draft: boolean = true callback_url: string | null ``` `ToolkitPayload` ```text task*: string input, media, output_name: string | null params: object = {} callback_url, export_target: string | null additional JSON fields are accepted ``` ### Transcription and Subtitles `TranscribePayload` ```text audio*: string path or URL model_size, language: string | null task: string = "transcribe" beam_size: integer = 5 vad_filter, word_timestamps: boolean = true ``` `SubtitlePayload` ```text events*: [{ "start": number, "end": number, "text": string }] format: string = "srt" template: string = "tiktok_classic" ``` `SceneBuildPayload` ```text script*: string assets*: string[] duration: number | null transition: string = "fade" creative_style: string | null ``` ## Framework and UI Endpoints These are not business API operations: | Path | Purpose | | --- | --- | | `/dashboard` | Renderer Gradio interface. | | `/tts/` | Kokoro TTS Gradio interface. | | `/music/` | MusicGen Gradio interface. | | `/whisper/` | Whisper Gradio interface. | | `/editor/` | FFmpeg editor Gradio interface. | | `/mcp/` | MCP Streamable HTTP transport; use an MCP client rather than a normal REST request. | | `/dashboard/gradio_api/mcp/` | Gradio-native MCP transport for documented dashboard actions. | | `/mcp-health` | MCP transport discovery and custom tool count. | | `/docs` | Root FastAPI Swagger UI when `ENABLE_API_DOCS=true`. | | `/redoc` | Root FastAPI ReDoc UI when `ENABLE_API_DOCS=true`. | | `/openapi.json` | Root renderer OpenAPI schema when `ENABLE_API_DOCS=true`. | | `//docs` | Mounted service Swagger UI when `ENABLE_API_DOCS=true`. | | `//openapi.json` | Mounted service OpenAPI schema when `ENABLE_API_DOCS=true`. |