3. Operations catalog

Every operation is addressed by a service_slug + operation_key pair and takes its input as params.file_url.

The live, always-current list is one public request (no key needed) — this is the same data the panel's catalog renders from:

curl -s https://mp.dvocorp.com/api/v1/studio/tools

Each entry gives you operation_id, service_slug, operation_key, credits_per_call, what file kinds it accepts, its tunable parameters (config.fields[], each with key / type / min / max), and its ready-made settings (config.presets[], each a {label, params} — the top-level presets[] list carries only {index, label}, so read the param values from config.presets[].params).

credits_per_call is what one job costs at submit, and it is the same on every tool because there is one tariff. A params.operations pipeline does not change it either: addressing a cheap operation while shipping an expensive recipe costs exactly the same as addressing the expensive one.

The tariff: you pay for processing time

One price, every operation — photo, video, audio, subtitles, batch, API, studio:

processing time you pay
up to 1 min 2
1–2 min 3
2–3 min 4
each further started minute +1
however long it runs never more than 30

A started minute is a whole minute: 61 seconds is two minutes. Processing time is measured on the worker from the moment it picks your file up — the queue wait is not billed.

Nobody knows how long your file will take until a worker has run it, so the flow is:

A job that ends failed or canceled is never charged for time and every bonus it took is refunded. If we cannot measure the time at all, you pay the base and nothing more.

Don't hardcode these numbers — GET /api/v1/public/config returns them under billing: time_tariff_base_credits, time_tariff_per_extra_minute_credits, time_tariff_max_credits, time_tariff_enabled.

The input-length ceiling

The 30-bonus cap bounds your bill; it does not bound our workers. So an input longer than your tier allows is refused, not truncated: the job ends failed, every bonus it took is refunded, and error names the length, the limit and your tier.

tier max input length, any operation
anonymous (no sign-in) 10 min
free account 30 min
paid plans 180 min

This applies to every operation, not just speech-to-text, and only to media that reports a duration (images are never affected). Live values are on GET /api/v1/public/config under billing.input_max_minutes.

Split a longer recording yourself (trim is a remux — it is cheap and lossless) and submit the parts.

Bulk works the same way, per file. A /studio/batch is charged the base per file at submit and each file's own processing time afterwards, and the length ceiling applies per file too. A file longer than your tier allows stops the batch: the remaining files are canceled, the batch ends failed with the filename in error, and everything not already delivered is refunded, the over-long file included. Files delivered before it stay charged.

Some jobs — compress, resize, convert, watermark, add/strip metadata — exist on both services, once for video and once for images. Those entries share a config.family value. The web studio uses it to show a single button and picks the operation from the file you dropped; over the API you address the two operations separately, as usual, by service_slug + operation_key. The tables below are the shipped baseline — an admin can add or retune operations, so treat /studio/tools as the source of truth.

ClipConvert — video ("service_slug": "clipconvert")

Every operation accepts video.

operation_key What it does Bonuses Main params
compress Shrink for web 3 level light|medium|strong, crf 0–51, preset ultrafast…slow
resize Change resolution 3 width / height 16–7680, mode fit|pad
convert Change container 3 format mp4|webm|mov|mkv; vcodec libx264|libx265|libvpx-vp9|copy, acodec aac|libmp3lame|libopus|copy (omit → the container's default)
social Social presets 3 preset square|vertical|widescreen, mode crop|pad, focus_x/focus_y 0–1 (where the crop sits: 0 = left/top edge, 0.5 centre)
trim Cut a segment 2 start (s), duration (s), precise (default true)
fps Change frame rate 2 fps 1–120
faststart Web-optimize (instant play) 1 —
mute Remove the audio track 1 —
speed Speed up / slow down 3 factor 0.1–10
rotate Rotate 2 mode 90cw|90ccw|180
flip Mirror 2 axis h|v
gif Video → GIF 3 start, duration 1–60, fps 1–30, width 16–1920
thumbnail Grab a frame 1 at (s), smart, format jpg|png|webp (jpeg is accepted as an alias for jpg), width 16–3840
cover Cover image 1 builder-driven
extract_audio Rip the audio 2 format mp3|aac
enhance_voice Clean up voice 3 level light|medium|strong; denoise, deess, compress, loudnorm
add_audio Voiceover / background music 3 tracks[], original_volume — see Add audio
subtitles Burn / embed subtitles 6 see Subtitles
transcribe Speech → SRT/VTT/TXT 5 format, language, task, quality fast|balanced|accurate
auto_edit Cut pauses & filler words 7 language, max_pause 0.2–5, keep_pause 0–2, padding 0–1, remove_fillers, remove_retakes
circle Round video / Telegram note 3 see Circle
blur_bg Fit to canvas, blurred bg 3 see Fit to canvas
watermark Overlay a watermark 3 builder-driven
device_meta Tag as shot on a device 2 preset (device key), creation_time — writes container/QuickTime tags, not EXIF
strip_metadata Remove metadata 1 —

From a link (input is a URL, not a file)

These operations take no uploaded file. The input is a public video link in params.url, so they are submitted with the plain JSON POST /api/v1/jobs instead of the multipart upload — everything else (polling, the result URL, billing) is identical.

operation_key What it does Bonuses Main params Ships
link_info Read a link's public metadata, engagement stats and comments 2 url, include[], comment_limit 1–500, format json|csv on
download_video Download the video 2 url, quality best|2160p|1440p|1080p|720p|480p|360p, start, duration off
download_audio Link → MP3 / M4A / Opus / WAV 2 url, audio_format, start, duration off
download_thumbnail The platform's own cover image 1 url, thumbnail_format jpg|png|webp off
link_transcribe Video link → text (fetch + Whisper) 5 url, language, transcript_format txt|srt|vtt|json, task off
link_captions Video link → video with burned-in captions 6 url, language, style karaoke|boxed|clean|yellow, position off

"Ships off" means exactly that. Every operation that TRANSFERS media from a platform is present in the code and dormant: platform terms of service forbid the transfer, and payment providers' acceptable-use policies name it. Turning one on takes two deliberate acts — an admin enables the operation, and the operator sets MEDIA_FETCH_ENABLED=true on the video service. Until then those operations answer with a plain "switched off on this server". Reading a link's public metadata (link_info) moves no media and is on by default.

# one request, no upload — the link IS the input
curl -s https://mp.dvocorp.com/api/v1/jobs/api \
  -H "X-Api-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{"service_slug":"clipconvert","operation_key":"link_info",
       "params":{"url":"https://www.youtube.com/watch?v=VIDEO_ID",
                 "include":["info","stats","analytics"],"format":"json"}}'
# -> {"id":"...","status":"queued"}   then poll /api/v1/jobs/<id> as usual

include[] picks what the report contains — any of info (title, description, channel, tags, upload date), stats (views, likes, comments, subscribers), analytics (engagement rate, views/day, likes and comments per 1k views, views per subscriber), chapters, subtitles (which caption languages exist), thumbnails, formats (available qualities), comments. format: "csv" flattens the comments — or, with no comments requested, the stats — into one spreadsheet-ready table.

Three limits worth knowing before you build against these:

Which video operations re-encode (and which don't)

An operation that changes no pixels is a remux: the picture is copied packet for packet instead of being re-encoded. It comes back in seconds instead of minutes, and it is bit-identical — no quality loss, no bitrate change.

Behaviour Operations
No encoder at all link_info (reads a page), download_video / download_audio / download_thumbnail (a transfer, at most a mux)
Remux — nothing is re-encoded faststart, mute, strip_metadata, device_meta, trim from 0 (or with precise: false), convert when the codecs already fit the new container (e.g. .mov H.264 → .mp4), subtitles with mode: "embed", extract_audio when the source track already is that format (AAC → aac)
Sound only — the picture is copied enhance_voice, add_audio
Full re-encode — required compress, resize, social, fps, rotate, flip, speed, watermark, blur_bg, circle, gif, subtitles with mode: "burn", auto_edit, convert to a different codec (anything → webm)

Two consequences worth knowing:

The finished job reports which path actually ran at result.body.encode_mode — one of remux, video_copy, audio_copy, encode.

PhotoConvert — images ("service_slug": "photoconvert")

Every operation accepts image.

operation_key What it does Bonuses Main params
resize Change dimensions 1 width / height 16–20000, resize_mode fit|fill|exact; scale by percentage needs both percent and resize_mode: "scale"
compress Shrink 1 quality 1–100, format jpeg|webp|avif|png, max_kb (re-encodes down to fit)
convert Change format 1 format webp|avif|jpeg|png, quality, max_kb
crop Crop to aspect 1 crop_aspect 1:1|4:5|5:4|4:3|3:4|3:2|2:3|16:9|9:16|21:9, gravity center|top|bottom|left|right
circle Round avatar 1 diameter, zoom, offset_x/y, bg_color (omit → transparent PNG)
transform Rotate / flip 1 rotate 0–359 counter-clockwise (90/180/270 lossless, other angles fill the corners with bg_color), flip h|v
enhance Filters 1 autocontrast, brightness / contrast / saturation / sharpness 0–4 (1 = unchanged), blur 0–100 px, grayscale
social Social sizes 1 any width/height 16–20000, resize_mode (presets use fill)
web_optimize Optimize for web 1 format, quality, max_kb, strips metadata
strip_meta Remove EXIF / GPS 1 strip_metadata, strip_gps; GPS only → {"metadata_mode": "merge", "strip_gps": true}
device_meta Write camera EXIF 2 preset (see below), metadata_mode: "set"
watermark Overlay a watermark 1 text or logo_url, position (9-grid tl…br), opacity 0.05–1, size (% of width), color
edit Everything in one pass 2 composes a full pipeline

device_meta presets: iphone, iphone_15, iphone_15_pro_max, iphone_16, iphone_16_pro, iphone_16_pro_max, samsung_galaxy_s24, samsung_galaxy_s24_ultra, samsung_galaxy_s25_ultra, google_pixel_8_pro, google_pixel_9_pro, xiaomi_14_ultra, oneplus_12, sony_xperia_1_vi, huawei_p60_pro, sony_a7_iv, canon_eos_r5, canon_eos_r6_mark_ii, nikon_z8, fujifilm_x_t5.

Send the params. An image operation whose params say nothing to do (watermark with no text and no logo, edit with no pipeline) is rejected with an error — it is never accepted, billed, and answered with your own file back.

There is no separate audio service. Audio work is done by the ClipConvert operations extract_audio, mute, enhance_voice and transcribe — and the input is a video file, not an audio file. These operations read the audio track out of a video container; handing them a bare .mp3/.wav/.aac is refused with 400 this operation works on video files — the file you sent looks like audio. To process a standalone audio file, first wrap it in a video container (e.g. a still image + the audio track) and submit that.