codai docs
Gateway

Media operations

codai Studio media operations — remove backgrounds, upscale, inpaint, separate stems, denoise, master, convert audio to MIDI and generate music or sound effects from your assets.

Media operations turn one Studio asset into new ones: cut out a subject, upscale a still, repaint a masked area, split a song into stems, clean up a voice, master a mix, convert a melody to MIDI or generate a song or a sound effect. Every op takes asset ids and strict per-op parameters, and every output is stored as a new asset in your library. The public model id is codai-studio-media.

MethodPathResult
GET/v1/studio/media-opsThe op catalogue: engine, mode, inputs, available, a list quote (free)
POST/v1/studio/media-ops/{op}/quoteThe exact price of a request; nothing is held or billed
POST/v1/studio/media-ops/{op}200 with the outputs (sync ops) or 202 with a job (async ops)
GET/v1/studio/media-ops/jobs/{id}An async job; a finished one is settled when you read it

{op} is the dotted op id, for example image.remove_background or audio.separate_stems.

The ops

OpInputsOutputModeEngine
image.remove_background1 imageCut-out PNG / WebP with transparencysyncBiRefNet
image.upscale1 imageImage ×2 or ×4 (at most 64 MP)syncReal-ESRGAN x4plus
image.inpaintimage + PNG maskThe image with the white mask area repainted from promptsyncGemini image edit
image.eraseimage + PNG maskThe image with the masked object removedsyncGemini image edit
image.outpaint1 imageThe scene extended to a new aspect_ratiosyncGemini image edit
audio.separate_stems1 audio4 stems (vocals, drums, bass, other) or 2 (vocals, accompaniment)asyncDemucs (htdemucs)
audio.denoise1 audioThe same audio with background noise removedasyncDeepFilterNet 3
audio.to_midi1 audioA Standard MIDI File (kind: "midi")asyncBasic Pitch
audio.master1 audioA loudness-normalised master (WAV or MP3)asyncffmpeg loudnorm, 2 passes + limiter
audio.analyze1 audioJSON only: BPM, key, loudness, peaksynclibrosa + pyloudnorm
music.generate_ace0–1 audioA song from a style prompt and optional lyricsasyncACE-Step 1.5
music.repaint1 audioThe song with one section replacedasyncACE-Step 1.5
music.extend1 audioThe song continued by extend_secondsasyncACE-Step 1.5
music.cover1 audioYour own song restyledasyncACE-Step 1.5
sfx.generatenoneA sound effect from a promptasyncACE-Step 1.5

Image ops accept image, upload and frame assets (PNG, JPEG or WebP); audio ops accept music, speech, sfx and upload assets. For image.inpaint and image.erase the second asset is a black-and-white PNG mask of the first: white is edited, black is kept. The mask is sent to the model as a reference image, so edits follow it closely but not pixel-exactly (the catalogue entry carries a note saying so).

Parameters

Parameters are validated strictly per op — an unknown key is a 400.

OpParams (defaults)
image.remove_backgroundformat: png | webp (png)
image.upscalescale: 2 | 4 (4)
image.inpaintprompt (required), model, image_size: 1K | 2K | 4K
image.eraseprompt (optional hint), model, image_size
image.outpaintaspect_ratio (required: 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, 9:21), prompt, model, image_size
audio.separate_stemsstems: 2 | 4 (4)
audio.denoisestrength: 0–1 (1)
audio.to_midionset_threshold: 0.05–0.95 (0.5), min_note_ms: 10–1000 (58)
audio.mastertarget_lufs: -14 | -16 | -23 (-14), true_peak_db: −3…−0.1 (-1), format: wav | mp3 (wav)
audio.analyzenone
music.generate_aceprompt (required, ≤ 1000 chars), lyrics (≤ 4000), duration_seconds: 10–240 (60), bpm: 40–220, key, language (en, ro, es, fr, de, it, pt, ja, ko, zh), seed
music.repaintprompt, lyrics, start_seconds, end_seconds (after the start, inside the song), seed
music.extendprompt, lyrics, extend_seconds: 5–120, seed
music.coverprompt, lyrics, strength: 0–1 (0.8), seed
sfx.generateprompt (required), duration_seconds: 1–30 (5), seed

Running an op

curl https://ai.codai.ro/v1/studio/media-ops/image.remove_background \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cutout-42" \
  -d '{ "asset_ids": ["3f2b8c1e-0000-4000-8000-000000000001"], "params": { "format": "png" } }'

A sync op answers 200:

{
  "object": "media_op.result",
  "op": "image.remove_background",
  "status": "ready",
  "outputs": [{ "asset_id": "…", "kind": "image", "mime_type": "image/png", "url": "https://…" }],
  "analysis": null,
  "cost_micro_usd": 2640
}

audio.analyze stores nothing and returns analysis: { duration_seconds, bpm, key, key_confidence, integrated_lufs, sample_peak_dbfs }.

Async jobs

Stems, denoise, MIDI, mastering and every music or sound-effect op run as jobs. The run answers 202 with { "object": "media_op.job", "id", "status": "pending", … }. Poll GET /v1/studio/media-ops/jobs/{id} until status is ready (with outputs, one asset per stem for separation) or failed (with error: timeout, input_refused, dispatch_failed, store_failed or an engine message). A job that has not produced a result after 20 minutes fails with timeout. Reading a finished job stores its outputs and bills it once; the background sweeper does the same when nobody polls. At most 3 async jobs run per account at once — a fourth answers 429 with Retry-After.

Pricing

Every op has a quote — the price ceiling — that is held on your wallet before anything runs; when the wallet cannot cover it the answer is 402 insufficient_funds and nothing starts. After the op the measured cost is billed once and the rest of the hold is released; a failed op releases the whole hold.

  • GPU / CPU ops (everything except the Gemini image edits) are priced by instance time on the codai Studio media service: $0.95 per hour, at least 5 seconds per op, estimated from the input length (for example audio.separate_stems ≈ 15 s + 0.5 s per input second). You pay the seconds the engine reports, never more than the quote.
  • image.inpaint / erase / outpaint are billed like an image edit of the chosen model and image_size.

GET /v1/studio/media-ops lists a reference quote per op (default params, 60 s of audio or a 1024×1024 image) as quote_micro_usd and the wallet amount as quote_micro_eur; POST …/{op}/quote prices your real request with the same validation as the run.

Errors

StatusWhen
400Bad body or params, the wrong number or kind of assets, a mask that is not PNG, an upscale over 64 MP, end_seconds before start_seconds
402 insufficient_fundsThe quote does not fit in your wallet
404Unknown op id, or an asset that does not exist, is not ready or is not yours
429Three async jobs already running, or your key's rate limit
502The engine failed
503 media_ops_unavailableThe op's lane is not configured on this gateway (available: false in the catalogue); nothing is held or billed
503 assets_unavailableAsset storage is not configured

AI marking and lineage

Every output is marked as AI-generated before it is stored: C2PA / XMP on images, RIFF INFO or ID3 tags on audio, plus the ai-generated object metadata. Each output also records where it came from — parent_asset_id (the first input), op and op_inputs (asset_ids + the validated params) — so you can always trace a stem, a master or a cut-out back to its source. Generate covers only from music you own or have the rights to.

SDKs

const job = await codai.mediaOps.run('audio.separate_stems', { assetIds: [songId], params: { stems: 4 } });
const done = await codai.mediaOps.wait(job.id); // or mediaOps.runAndWait(...)
done = client.media_ops.run_and_wait("audio.master", [mix_id], {"target_lufs": -14})

TypeScript: mediaOps.list / quote / run / job / wait / runAndWait. Python: media_ops.list / quote / run / job / wait / run_and_wait. Songs and podcasts built from these outputs are edited as audio creatives and rendered to a mastered mix with creatives.renderAudio; lyrics come from the lyrics writer.

On this page