# HF Studio **Your AI studio for video, image and audio.** [![CI](https://github.com/Jjat00/hf-studio/actions/workflows/ci.yml/badge.svg)](https://github.com/Jjat00/hf-studio/actions/workflows/ci.yml) [![M8ven Score](https://m8ven.ai/badge/mcp/jjat00/hf-studio)](https://m8ven.ai/mcp/jjat00/hf-studio) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](pyproject.toml) [![MCP](https://img.shields.io/badge/MCP-41%20tools-8A2BE2.svg)](#connect-your-agents-mcp) [Español](README.es.md) · **English** · [Website and examples](https://hf-studio.jato.run) **A self-hosted AI video, image and audio studio on top of the [Higgsfield](https://docs.higgsfield.ai) and [ElevenLabs](https://elevenlabs.io/docs) APIs.** One backend gives you a REST API, an **MCP server** so your agents (Claude Code, Codex, Claude Desktop, ChatGPT…) can generate for you, and a **web UI** to do it by hand. Everything lands in the same library. Its MCP server is built around one rule: **an agent cannot spend credits without first quoting that exact request**, so it always has a price to show you before it generates. ![HF Studio home: featured models, AI Influencer, one-click Apps and shortcuts](docs/img/portada.webp) ## Why - **The cheapest provider for each video and image:** the same model through Higgsfield, APIMart or KIE, quoted on all of them, with a safe fallback (see [Cheaper with more providers](#cheaper-with-more-providers)). - **One place for 91 Higgsfield endpoints** (Seedance, Kling 4.0, Wan, Hailuo, PixVerse, LTX, Grok Imagine, Ideogram, Recraft…), searchable by what they do (text-to-video, first/last frame, video edit, motion transfer…) instead of by brand, **plus 156 models that only APIMart and KIE sell** (Nano Banana, GPT Image, Midjourney, Seedream, FLUX, Veo 3.1, Vidu, Runway…). - **Agents that quote before they spend.** Every MCP tool that starts a paid run requires a single-use `quote_id` from a prior quote of the same request, and the server tells the agent to show you that cost and wait for your OK. - **Persistent jobs.** Async jobs stored in a database, with validation before spending, deduplication, `Idempotency-Key`, a local queue that respects your account's concurrency, and local copies of every output (Higgsfield only keeps them for 7 days). Higgsfield jobs keep being tracked after a restart; audio jobs cut off by a restart are marked as failed, not resumed. ## Cheaper with more providers The same model is often sold by several providers at very different prices. HF Studio can send each generation to the cheapest provider that offers **the exact same model and settings**: | Provider | Key | What it adds | | --- | --- | --- | | [Higgsfield](https://higgsfield.ai) | `HF_API_KEY`, required | Every catalog model, and it stores your input files (uploading is free) | | [APIMart](https://apimart.ai) | `APIMART_API_KEY`, optional | Usually the cheapest for Seedance, Wan and Kling Omni/O3; also Grok Image 2.0, and store-only models such as Nano Banana, GPT Image, Midjourney, Veo 3.1, Vidu and SkyReels | | [KIE](https://kie.ai) | `KIE_API_KEY`, optional | Usually the cheapest for Hailuo, MiniMax, Kling 2.5/2.6, Kling Motion Control 2.6 and Z-Image, and store-only models such as Nano Banana, GPT Image, Veo 3.1, Seedream, Ideogram 3, Runway and talking avatars | - **Models Higgsfield does not have.** With an APIMart or KIE key, the catalog also offers 156 models that only those stores sell (75 image, 81 video), marked "APIMart / KIE" in the UI and `store: true` in the API: Nano Banana 1, 2, 2.1, 2 Lite and Pro, Imagen 4, GPT Image 1.5, 2 and 2.5, GPT-4o Image, Midjourney, Seedream 3 to 5, FLUX.2, FLUX 3 and Kontext, Qwen Image 2, Wan 2.7 Image, Ideogram 3, older Grok Imagine channels, Veo 3.1, Gemini Omni, Seedance 1.0 to 2.0 Fast/Mini, Vidu Q3/Q4, Hailuo 02, SkyReels V4, Runway, Wan 2.2/2.5, Kling 2.1, talking avatars (Kling AI Avatar, InfiniteTalk, OmniHuman) and upscalers. Each family is one module in `src/hf_studio/store/`, with the same fidelity rule and quoting as the rest. - **Quote every provider, run the cheapest.** `/v1/estimate`, the UI and `estimate_cost` show every option, the savings against Higgsfield and why a provider was left out (no key, no balance, or no exact equivalent for your settings). Prices come from each reseller's public price list, refreshed daily. On 2026-10-03, Seedance 2.0 at 720p for 5 s cost 0.71 USD on APIMart, 1.025 on KIE and 1.51 on Higgsfield. - **Same model, never a substitute.** A setting a provider cannot reproduce (a fixed seed, a bitrate, an aspect ratio…) leaves that provider out instead of being dropped. - **Unofficial channels, labeled.** A provider may also sell a cheaper unofficial channel of the same model (e.g. APIMart's `grok-imagine-1.5-video-ext` for Grok Imagine 1.5, about 6× cheaper). It is one more option, marked "unofficial channel"; if it fails, the official one is the fallback under the usual approval rule, and once you approve a channel that is the one sent. - **Every generation says what it cost.** The history shows the provider, the provider's model (and whether the channel is unofficial) and the cost in USD (`~` when estimated); failed generations show no cost. - **Kling 3.0 elements.** Save characters, products or locations on the **Elements** page (or with `create_element`): a name, a description and 2 to 4 JPG/PNG images, stored locally and shared by the UI and your agents. Pick them in Kling 3.0 (up to 3) and cite them in the prompt as `@name`. They run on APIMart and KIE (KIE also needs a first frame); Higgsfield's public API cannot create or read elements, so those requests skip it. - **Fallback without double charges.** If a provider rejects the request or the task fails without charging, the next one is tried on its own only if it costs the same or less than what you approved; otherwise the generation waits for your approval. A submission that may have reached the provider (timeout, unclear answer) is never retried anywhere. - **Prices checked again before sending.** APIMart and KIE are priced from local tables, so they are re-priced right before every send; Higgsfield quotes are reused for 15 minutes and asked again after that. Before the first automatic send the whole plan is redone with current prices. The generation stops for your approval when the checked price goes over your approved cap, when the price becomes unknown and you did not accept an unknown cost for that option, or when an upfront hold (some providers hold more than they charge and refund the difference) goes over the hold you approved. If you accepted an unknown cost without a cap for an option, that option can run at whatever price it ends up having; holds are always approved separately. - **Add a key:** `uv run hf-studio providers --add apimart` (or `kie`) asks for it, checks it for free and saves it; `--open NAME` opens the page where you create it, and `hf-studio providers` shows status and balances. The UI has a **Providers** page with the same links. - **Add a provider (developers):** write a `Provider` subclass in `src/hf_studio/providers/` (key check, submit, poll, optional price list) plus its model table, and list it in `registry.py`. Setup, the UI, the MCP and the worker pick it up from there. ## Features - **Spaces, a node canvas to generate step by step** (`/spaces`, inspired by Magnific Spaces): connect texts, media, lists and any catalog model, and each step's output feeds the next one. Every node shows its cost in USD before you generate and keeps its run history. Right-click or press `/` to add a node; drag an output to an empty spot to add the next step already connected. - **Runs on the server** ("Run all" or "Generate this and what follows"): every step is quoted, you approve a total budget, and the run pauses to ask you if a step does not fit, has no price or its fallback provider costs more. It keeps going if you close the tab and never pays twice after a restart. - **Lists for batches:** a generator connected to a list runs once per checked item, and the batch follows the chain in pairs (5 prompts → 5 images → 5 videos). - **Free local tools** (ffmpeg): first or last frame of a video, combine videos, mix audio over a video. - **Audio nodes** (ElevenLabs): voiceover with your voices, sound effects and music. - **Assistant node** (Claude, optional `ANTHROPIC_API_KEY`): writes or improves prompts, describes images and drafts scripts; its text feeds other nodes. - **Flows:** publish a canvas with labeled inputs and run it from Presets by filling a form. - **AI Influencer** (`/influencer`): design your own character from traits, with Higgsfield's catalog (9 character types, from normal to animal, and 18 trait groups: face, hair, body, style, accessories…) and its 39 examples, each one ready to recreate. By default the sheet comes from Higgsfield's own AI Influencer model ($0.05, traits sent by key from its live catalog; animal types still fail on Higgsfield's side), or from a prompt with Soul 2.0, Soul Cinema, Sunburst, Flare, Grok Imagine, Qwen Image, Z-Image, Recraft or Ideogram, optionally from your own photo. Then animate it the way Higgsfield does, with Genjutsu or Genjutsu Light copying the motion of a video of yours, or with other video models and 8 movements. Every step is quoted before it runs. - **One-click Apps:** relight, change color palette, face swap, character swap, reframe, video relight, video color grade and ad multiplier, each a built-in preset over a catalog model: upload, adjust and generate. - **91 Higgsfield endpoints** (73 video, 17 image, 1 custom references), each with its JSON Schema taken from the official docs (`hf-studio sync-catalog`; Kling 4.0, Genjutsu Light and AI Influencer, only on open.higgsfield.ai, are written by hand and kept by the sync), and 156 store-only models from APIMart and KIE. - **Every video and image capability:** text-to-video, image-to-video, first and last frame, reference videos, video edit and extend, motion transfer, object swap and restyle (Genjutsu: anime, stop motion, 3D… keeping the motion and the audio, with Higgsfield's current styles), text-to-image, image editing and ad images (Ads Studio: 1 to 8 ads from a brief and your product photos). - **ElevenLabs audio (optional):** change the voice on a segment of a video (with deep, monster and ghost effects), text-to-speech, sound effects, music and voice isolation. - **Keep the original audio** when editing a video: HF Studio muxes the source audio back onto the result (free, with ffmpeg), handy when the model returns a silent clip. - **Cost visible before generating:** next to the button in the UI, and required by the MCP tools (one quote per request). On the REST API, the ElevenLabs routes require their own quote; the Higgsfield routes offer `/v1/estimate` and `dry_run` and trust whoever holds the `hfs_…` key. - **Library** in a masonry grid with everything generated from the UI and from agents (with its origin), filters and a detail page per result with its full configuration (model, prompt, parameters, input files and JSON). - **Sound library:** all your ElevenLabs sounds (including voices made on their website, imported for free) sorted by kind (voices, screams, laughs, creatures, ambience, impacts, foley, transitions, music) with editable titles and tags, so you can reuse them instead of paying again. - **Presets** (recipes with variables), **batches** with a total quote, a natural-language **model recommender** (English and Spanish) and **24 use cases** with step-by-step tutorials for the UI and for agents. - **Bilingual UI:** Spanish by default, English one click away. | AI Influencer and Apps | Features and Models menus | | --- | --- | | ![AI Influencer banner and one-click Apps](docs/img/influencer-apps.webp) | ![Video menu with Features and Models](docs/img/menu.webp) | | **Video studio** | **Use cases** | | ![Video studio with its cost before generating](docs/img/estudio-video.webp) | ![Use cases](docs/img/casos-de-uso.webp) | ## Requirements Runs natively on Windows, macOS and Linux (and in WSL). CI tests the backend on all three. - Python 3.12+ and [uv](https://docs.astral.sh/uv/) - Node.js 20+ and [pnpm](https://pnpm.io/) (for the UI) - [ffmpeg](https://ffmpeg.org/) with `ffprobe` (voice change, original audio and isolation; also used by the tests). Linux: `sudo apt install ffmpeg`. Windows: `winget install ffmpeg`. macOS: `brew install ffmpeg-full`, because the plain `ffmpeg` formula lacks the `rubberband` filter that the voice effects use (HF Studio finds `ffmpeg-full` on its own and warns at startup if the filter is missing). - A Higgsfield API key ([console.higgsfield.ai](https://console.higgsfield.ai)). Generating spends your credits. - Optional: APIMart and KIE API keys, to make videos cheaper (see [Cheaper with more providers](#cheaper-with-more-providers)). - Optional: an ElevenLabs API key ([elevenlabs.io/app/settings/api-keys](https://elevenlabs.io/app/settings/api-keys)) for voice, effects and music. Pay-as-you-go balance or a plan both work. ## Quick start ```bash git clone https://github.com/Jjat00/hf-studio.git cd hf-studio ./dev.sh # macOS, Linux, WSL .\dev # Windows (cmd or PowerShell) ``` Both are shortcuts for `uv run hf-studio start`, which works the same on Windows, macOS and Linux. The first run asks for your Higgsfield key (and, optionally, your ElevenLabs key), validates it without spending credits, creates `.env` and the UI key, installs the dependencies and starts everything. To add the ElevenLabs key later, put it in `ELEVENLABS_API_KEY` in `.env` and restart. - Web UI: http://localhost:3000 - API and interactive docs: http://127.0.0.1:8787/docs Only want to use it from your agents? `./dev.sh api` (Windows: `.\dev api`, or anywhere `uv run hf-studio start --api-only`) starts just the API, no Node.js needed. Then connect your agent in one line (see [Connect your agents](#connect-your-agents-mcp)): ```bash uv run hf-studio connect claude-code # or: codex, claude-desktop, json ``` Setting it up with an AI agent? Point it to [AGENTS.md](AGENTS.md). ### Commands | Command | Use | | --- | --- | | `hf-studio start [--api-only]` | Sets up on the first run and starts the API and UI (or only the API); `./dev.sh` and `dev.cmd` run it | | `hf-studio setup [--no-input]` | First-run setup: `.env`, key check and UI key (`start` runs it) | | `hf-studio connect CLIENT [--print]` | Registers the MCP server in `claude-code` or `codex`, or prints the config (`claude-desktop`, `json`) | | `hf-studio serve` | Starts the API and the worker | | `hf-studio cloud` | Docker image entry point: API and built UI behind `STUDIO_PASSWORD` | | `hf-studio mcp` | MCP server over stdio | | `hf-studio create-key NAME` · `list-keys` · `revoke-key NAME` | One `hfs_…` key per client (UI, Claude Code, Codex…) | | `hf-studio see-all NAME [--off]` | Lets a client see and manage everyone's generations (meant for the UI) | | `hf-studio keep-source-audio ID` | Puts the source video's audio on an existing edit | | `hf-studio providers [--add NAME \| --open NAME]` | Providers' status and balance; adds a key or opens the page to create it | | `hf-studio check-credentials` | Validates the Higgsfield key without spending | | `hf-studio sync-catalog` | Regenerates `catalog.json` from docs.higgsfield.ai | ### Deploy it to your own cloud The `Dockerfile` builds a single image with the API, the built UI and ffmpeg. It starts with `hf-studio cloud`, which serves the UI on `$PORT` and keeps the API on `127.0.0.1` inside the container. Your keys live only in your server's environment variables. - **`STUDIO_PASSWORD` is required** (12+ characters): the UI is on the internet and spends your credits, so every page and API call asks for it. Without it the container does not start. - `HF_API_KEY` is required; `ELEVENLABS_API_KEY`, `APIMART_API_KEY`, `KIE_API_KEY` and `ANTHROPIC_API_KEY` are optional, as in `.env.example`. - Mount a volume on `/data` (SQLite database and your files). Without it, each redeploy starts empty. On **Railway**: create a project from this repository (it reads `railway.json`), add the variables above and a volume mounted at `/data`, and set `RAILWAY_RUN_UID=0` so the container can write to it. Any Docker host works the same way: ```bash docker build -t hf-studio . docker run -p 3000:3000 -v hf-studio-data:/data \ -e HF_API_KEY=... -e STUDIO_PASSWORD=... hf-studio ``` Webhooks are not used in this mode (jobs are polled), and MCP agents keep connecting to a local HF Studio. ### Keys and access Each client has its own `hfs_…` key and, by default, only sees its own generations. Only the API knows the Higgsfield and ElevenLabs credentials. > **Warning:** the UI proxy does not authenticate visitors, so with `see-all` anyone who can reach the Next server can > see, delete and launch generations. `pnpm dev` and `pnpm start` listen on `127.0.0.1` only; do not expose it to > other machines without putting authentication in front. To put it online, use `hf-studio cloud` and > `STUDIO_PASSWORD` ([Deploy it to your own cloud](#deploy-it-to-your-own-cloud)). ## Connect your agents (MCP) With the API running (`hf-studio start`, `./dev.sh` or `.\dev`), the easiest way is to **ask your agent to connect itself**. Paste this into Claude Code, Codex or any other agent (the UI's MCP page has it with your real path filled in): ```text Connect yourself (this agent) to the HF Studio MCP server. It is installed at: /absolute/path/to/hf-studio 1. Check that its API is running: GET http://127.0.0.1:8787/health must answer {"ok": true, ...}. If it does not, ask me to start it with `uv run hf-studio start` in that folder and wait. 2. Register it with the command for your client: - Claude Code: uv run --directory '/absolute/path/to/hf-studio' hf-studio connect claude-code - Codex: uv run --directory '/absolute/path/to/hf-studio' hf-studio connect codex - Any other MCP client: uv run --directory '/absolute/path/to/hf-studio' hf-studio connect json, and add the config it prints to your MCP configuration. The command creates a key just for you. If it says hf-studio is already registered, ask me before removing the old one. If your sandbox does not let you run it, show me the command so I can run it. 3. Tell me to restart you so you load the tools. 4. Read the "Use it through MCP" section of the AGENTS.md in that folder. Key rule: before generating, quote the cost, tell me and wait for my OK. ``` Or do it yourself: one command creates a key for the agent and registers the server: ```bash uv run hf-studio connect claude-code # runs `claude mcp add` for you uv run hf-studio connect codex # runs `codex mcp add` for you uv run hf-studio connect claude-desktop # prints the JSON for claude_desktop_config.json uv run hf-studio connect json # prints the config for any other stdio MCP client ``` With `--print`, it prints the command or config (with a new key) instead of registering it. If the agent's CLI is not installed, nothing is registered: it prints the command, with its new key, to run where that CLI is. From WSL, `claude-desktop` prints a config that enters WSL through `wsl.exe`, for when HF Studio runs in WSL and Claude Desktop on Windows. Restart the client afterwards so it loads the tools. Each connection gets its own revocable `hfs_…` key (`codex`, `codex-2`…; see `hf-studio list-keys`, `revoke-key NAME`); existing keys are never touched. If the agent already has `hf-studio`, `connect` stops: remove it first (`claude mcp remove hf-studio`). By hand, the server is `uv run --directory /absolute/path/to/hf-studio hf-studio mcp` with `HF_STUDIO_URL=http://127.0.0.1:8787` and `HF_STUDIO_TOKEN=hfs_…` in its environment. Tools (41). Every tool declares all four MCP hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), and the ones that spend credits say so in their title, so clients can warn you before calling them. | Group | Tools | | --- | --- | | Models | `find_models`, `get_model`, `recommend_models`, `list_styles`, `list_model_options` | | Generate | `upload_media`, `estimate_cost`, `generate`, `generate_batch` | | Tracking | `get_generation`, `wait_generations`, `list_generations`, `cancel_generation`, `download_outputs`, `approve_fallback` | | Providers | `providers_status` | | Presets | `list_presets`, `run_preset`, `save_preset` | | Voice (ElevenLabs) | `list_voices`, `change_voice` | | Audio (ElevenLabs) | `text_to_speech`, `sound_effect`, `compose_music`, `isolate_voice`, `elevenlabs_account` | | Sound library | `list_sounds`, `label_sound`, `import_elevenlabs_history` | | Reuse and elements | `use_output`, `list_elements`, `create_element`, `delete_element` | | Spaces | `list_spaces`, `get_space`, `create_space`, `update_space`, `estimate_space_run`, `run_space`, `get_space_run`, `approve_space_run`, `cancel_space_run` | **No MCP tool generates without quoting that same request first.** `estimate_cost`, and `generate_batch` or `run_preset` with `dry_run=True`, return the cost and a `quote_id`. `generate`, and batches and presets with `dry_run=False`, require that `quote_id` with the same parameters. The ElevenLabs tools follow the same pattern: without `quote_id` they return the cost; with it, they generate. Each quote is good for one run and lasts 15 minutes; a retry with the same `idempotency_key` is not charged again. If the price is incomplete (missing `input_video_seconds`, which in batches can also go per item in `hints`), `confirm_unknown_cost=True` is required too, only when the user explicitly accepts an unknown cost. **Approving a pending generation is a separate step without a `quote_id`:** a generation in `awaiting_approval` (its provider failed without charging, or the price went over what was approved) shows the new price in `cost_usd` and any upfront hold in `reserve_usd`; with the user's OK, `approve_fallback` takes `max_usd` (that price), `max_reserve_usd` (that hold) or, only if the user accepts an unknown cost, `accept_unknown_cost=True` (without `max_usd` it has no cap; with it, the cap still rules once the price is known). HF Studio quotes again when approving and answers 409 with the current price if it went up. `get_model` also returns `studio_notes`, HF Studio warnings such as `generate_audio=false` giving a silent video on an edit and how to keep the original audio (`generate` with `keep_source_audio=true`). The MCP server never sees the Higgsfield or ElevenLabs credentials, only its own `hfs_…` key. It cannot check that a person actually saw the price: showing it and waiting for an OK is up to the agent, which the server instructions require. **Spaces follow the same rule.** `estimate_space_run` quotes every step and the total and returns a `quote_id`; `run_space` needs it and uses that total as the run's budget (a higher `max_total_usd` only if the user approved it). Retrying `run_space` with the same `quote_id` returns the same run. A run in `awaiting_approval` shows its reason and the new total in `pause.needed_total_usd`; with the user's OK, `approve_space_run`. A published flow runs with `inputs`. ## Web UI (`web/`) Next.js 16 and Tailwind 4, with a look inspired by higgsfield.ai. The logo, name and illustrations are original. ![Model catalog, searchable by capability](docs/img/modelos.webp) - **Navigation** like higgsfield.ai: Explore, Image, Video and Audio open a menu split into **Features** and **Models**, followed by MCP, Genjutsu, Spaces, AI Influencer, Apps, Use cases and Presets; the rest lives under **More**. - **Home** (`/`): featured models, a promo with shortcuts, the AI Influencer banner, one-click Apps, a prompt bar, what your agents can do and your latest creations. - **Pages:** Image (Create, with Ads Studio ads · Edit), Video (Create: text, first and last frame, references · Genjutsu: motion, object swap and restyle with a style picker · Edit video · Motion), Voice, Audio, Sound library, AI Influencer (`/influencer`), Elements (`/elements`), Use cases, Presets and Apps (with published flows at `/flows/`), Spaces (`/spaces`), Library (with detail at `/history/`), Usage (`/usage`: spend by day, provider and model, plus each provider's balance), Models (`/models`) and MCP. - **Forms** are generated from each model's `input_schema`, so all 240 models work without model-specific code. - **Voice** (`/voice`): pick a video, mark the segment on the timeline, search a voice (your account or the ElevenLabs public library, with preview), add an effect and choose how much of the original audio stays. - **Audio** (`/audio`): text-to-speech (expressive v3, stable v2 or cheap Flash, with language), sound effects, music and voice isolation from a video or audio file. - **Proxy:** the browser only talks to `/api/studio/*`, a proxy on the Next server that adds the `hfs_…` key. The key never reaches the client. - **Languages:** stored in the `hfs-lang` cookie, so URLs do not change. The API and the MCP server stay in English. ## REST API Every `/v1` route requires `Authorization: Bearer hfs_…`. Interactive docs at `http://127.0.0.1:8787/docs`. | Method | Route | Use | | --- | --- | --- | | GET | `/v1/models?capability=&output=&q=` | Filterable catalog (capability list included) | | GET | `/v1/models/{id}` | `input_schema`, usage notes, `studio_notes`, docs link | | GET | `/v1/model-styles/{id}` | Current styles of a model that takes a `preset_id` (Genjutsu Restyle) | | POST | `/v1/uploads` (multipart `file`) | Uploads jpg/png/webp/gif/mp4/wav → public URL | | POST | `/v1/estimate` `{model, input, hints}` | Cost before generating: exact, approximate by formula or pending media | | POST | `/v1/generations` `{model, input, keep_source_audio}` | Enqueues; 202 new, 200 if deduplicated. `Idempotency-Key` header | | GET | `/v1/generations/{id}?wait=60` | Status; waits up to N s for a terminal state | | GET | `/v1/generations?ids=a,b&wait=60` | History, or wait for several to finish | | POST | `/v1/generations/batch` | Several generations; `dry_run` returns cost per item and total | | POST | `/v1/generations/{id}/cancel` | Only in `pending`/`queued` | | DELETE | `/v1/generations/{id}` | Deletes a finished generation and its local copy | | GET | `/v1/generations/{id}/files/{name}` | Local copy of the output | | GET | `/v1/recommend?task=…` | Suggested models for a task (en/es) with their cost | | GET/POST/DELETE | `/v1/presets` | Built-in and custom recipes; `POST /v1/presets/{slug}/run` (with `dry_run`) | | POST | `/v1/presets/from-generation/{id}` | Save a generation as a preset | | GET | `/v1/voice/status` | ElevenLabs configured?, plan and remaining credits | | GET | `/v1/voice/voices?search=&library=` | Account voices or the public library | | POST | `/v1/voice/estimate` · `/v1/voice/changes` | Voice change on a segment: quote (`voice_quote`) and run with it | | GET/PATCH | `/v1/sounds?category=&q=` · `/v1/sounds/{id}` | Sound library with counts per category; fix title, category and tags | | GET/POST/DELETE | `/v1/elements` · `/v1/elements/{id}` | Kling 3.0 elements: list, create (multipart `name`, `description`, 2–4 `files` or own `image_urls`) and delete | | POST | `/v1/sounds/import-elevenlabs` | Imports the voices in your ElevenLabs history (free) | | POST | `/v1/audio/{service}/estimate` · `/v1/audio/{service}` | `text-to-speech`, `sound-effects`, `music`, `voice-isolator`: quote (`audio_quote`) and run with it | | GET/POST/PUT/DELETE | `/v1/spaces` · `/v1/spaces/{id}` | Node canvases (graph validated, saved with `version`); `flow` publishes one | | POST | `/v1/spaces/{id}/runs` | Run on the server: `dry_run` quotes every step and the total; with `max_total_usd` it starts (`Idempotency-Key`) | | GET/POST | `/v1/spaces/{id}/runs/{run}` · `…/approve` · `…/cancel` | Follow, approve a pause (new total) or stop a run | | GET | `/v1/flows` | Published flows with their inputs | States: `pending` (local queue) → `submitting` → `queued` → `in_progress` → `completed` | `failed` | `nsfw` | `canceled` | `timed_out`. Every response includes a readable `stage`, `terminal`, `elapsed_seconds` and `correlation_id` (for Higgsfield support). ElevenLabs jobs live in the same table and use the same `/v1/generations` routes. Example, a video between two images: ```bash curl -X POST localhost:8787/v1/generations -H "Authorization: Bearer $HFS" -H 'Content-Type: application/json' \ -d '{"model":"bytedance/seedance-2.0/image-to-video", "input":{"image_url":"https://…/start.png","end_image_url":"https://…/end.png", "prompt":"slow dolly-in","duration":5}}' ``` ## Design decisions - **Plain REST (httpx) instead of the SDK:** the generation POST is not idempotent, so after an ambiguous timeout it is **not retried** (the job becomes `failed/submission_ambiguous`). The worker owns the polling loop, with state in the database. - **Concurrency:** Higgsfield answers 400 at the limit and sends no `Retry-After`. Jobs wait in `pending` and are sent when there is room (`HF_MAX_CONCURRENCY`). - **Webhooks:** no documented signature, so a webhook only triggers an authenticated status check (its payload is not trusted). Each job carries its own token in the URL. Polling stays as a fallback. - **In-process worker:** one process is enough. The atomic claim (`pending → submitting`) prevents double submissions if several run. A restart mid-submission marks the job as ambiguous instead of repeating it. - **Media opened by ffmpeg:** only your own uploads or outputs, and only over `file` and `https` (no arbitrary URLs, to avoid SSRF). ## Development ```bash uv run pytest # Higgsfield and ElevenLabs are mocked with httpx.MockTransport: no credits spent uv run ruff check src tests cd web && pnpm lint && pnpm exec next typegen && npx tsc --noEmit ``` ## Contributing Issues and pull requests are welcome. If HF Studio is useful to you, a star helps other people find it. ## License [MIT](LICENSE). ## Disclaimer Personal project, not affiliated with Higgsfield or ElevenLabs. It uses their public APIs with your own keys and credits. Higgsfield, ElevenLabs and the model names belong to their owners.