Guides
How to Keep Original Video Sound with an AI Music and SFX API
- Written by
- Sonilo Team
- Published

Choose the source-audio policy before you call `POST /v1/video-to-video-sound`: omit both preservation fields to replace the source track, set `preserve_speech=true` to keep isolated speech, or set `keep_original_sound=true` to keep the whole original track. Add `ducking=true` only when a voice source is present. After the task succeeds, accept the result only if it is a nonempty video and, when source audio was required, its result includes a nonempty `music_processed` record.
By Sonilo Team · Facts verified August 25, 2026
Disclosure: Sonilo publishes this guide and operates the API used as the concrete example. The `video-audio-policy-gate-v1` module was tested with deterministic local fixtures. The tests did not call a production account, generate media, evaluate speech isolation, measure audio quality or latency, verify billing, or review a customer’s rights.
Decide whether the source track should survive
The current `video-to-video-sound` reference describes one asynchronous endpoint that generates music and sound effects from a video, combines them, and returns a finished video file. Its default is destructive to the source audio: when no preservation field is set, the returned video contains generated music and sound effects, while the source track is removed.
That default is useful for silent clips or footage whose camera audio should be replaced. It is unsafe as an unstated assumption for interviews, tutorials, product demonstrations, live events, or any video where dialogue, room sound, or an on-camera action matters.
Use one explicit policy per job:
| Required outcome | Request fields | Application acceptance rule |
|---|---|---|
| Replace source audio | Omit `preserve_speech`, `keep_original_sound`, and `ducking` | Require generated music, SFX, and a nonempty video; expect no `music_processed` |
| Keep speech only | `preserve_speech=true`; add `ducking=true` only when wanted | Also require a nonempty `music_processed` record |
| Keep the whole source track | `keep_original_sound=true`; add `ducking=true` only when wanted | Also require a nonempty `music_processed` record |
The endpoint’s OpenAPI 1.0.0 contract says `keep_original_sound` supersedes `preserve_speech` when both are present. A production client should avoid sending both. The server has a precedence rule, but an ambiguous request is still harder to review, log, test, and reproduce.
If you only need to mix an existing voice track with an existing music bed, use the Audio Ducking API. If you only need effects that follow visible events, use the Video-to-Sound-Effects API. The combined finished-video endpoint is for a different deliverable: one video file with newly generated music and sound effects already muxed into its picture.
Understand what ducking changes
`ducking` does not choose which source audio survives. It chooses how an available voice source is mixed with the generated layer.
The endpoint reference defines the voice source as the whole original track when `keep_original_sound=true`, isolated speech when only `preserve_speech=true`, or nothing when neither field is set. With a voice source, the default static mix keeps it forward; `ducking=true` dynamically lowers the generated music around that voice source before the music is combined with the sound effects. Without a voice source, ducking has no effect.
| Source policy | `ducking=false` | `ducking=true` |
|---|---|---|
| Replace | Generated music and SFX only | Meaningless; reject this combination in the client |
| Speech only | Isolated speech in a static mix | Isolated speech with dynamic ducking |
| Keep all | Whole original track in a static mix | Whole original track with dynamic ducking |
The public interface does not expose threshold, ratio, attack, or release controls. If a project requires exact mix automation, loudness targets, or manual handling of individual source moments, keep the audio layers separate and use a reviewed editing or media-processing pipeline. The official FFmpeg filter reference documents `amix` and `sidechaincompress` for teams that want to build and test that control themselves.
Submit one finished-video job
Keep the bearer key on a trusted server. The Sonilo API introduction identifies `https://api.sonilo.com/v1` as the production base and uses multipart form data for generation requests. For a browser-facing application, apply the separate Next.js server-boundary pattern or an equivalent backend boundary.
This request keeps the whole source track and asks for dynamic ducking:
- `curl -X POST https://api.sonilo.com/v1/video-to-video-sound \`
- ` -H "Authorization: Bearer $SONILO_API_KEY" \`
- ` -F "video_url=https://example.com/cut.mp4" \`
- ` -F "keep_original_sound=true" \`
- ` -F "ducking=true" \`
- ` -F "music_prompt=restrained documentary underscore" \`
- ` -F "sfx_prompt=natural ambience matched to visible events"`
A successful submission returns `202` and a `task_id`. Store that ID before doing anything else. The endpoint is asynchronous, and the task ID is the stable handle for later reads.
Before submission, validate the video rather than trusting its filename or URL. The video preflight guide covers file bytes, video-stream presence, duration, and live account upload limits. For signed remote inputs, use the source-URL lifetime gate so the provider still has time to fetch the asset after it enters the queue.
Check `GET /v1/account/services` before accepting work. The List Services reference says the authenticated response exposes available services, requests per minute, concurrent-generation capacity, upload size, and trial information when present. Do not promise a route, capacity, or free run from a cached marketing value.
Start with `variants_num=1`. The endpoint reference says variants can range from 1 to 10, cost scales with the count, and values above one are not covered by the free trial. Treat every increase as a new budget decision, not a harmless formatting option.
Separate submission success from output acceptance
Poll `GET /v1/tasks/{task_id}` with a bounded schedule. The Retrieve Task reference documents `processing`, `succeeded`, and `failed`, while the OpenAPI agent contract also tells adapters to treat `completed` and `canceled` as terminal. Download media only after a success state.
The provider documents voice isolation and the duck/mix pass as best-effort. If either fails, the task can silently degrade to generated music and sound effects alone instead of failing. That is a reasonable service fallback for a silent clip. It is not an acceptable product result when the job requirement was “keep the interview” or “retain the camera track.”
The current task schema exposes the signal needed for a strict application rule. Each combined-sound entry in `outputs` carries `output_url`, `output_type`, `output_bytes`, `music`, and `sfx`. It includes `music_processed` when a voice source was in play. Therefore:
| Provider state | Source-audio requirement | Product decision |
|---|---|---|
| Success, video is nonempty, `music` and `sfx` are nonempty, no preservation requested | Replace | Accept for playback review |
| Success and `music_processed` is nonempty | Speech only or keep all | Accept for playback review |
| Success but `music_processed` is absent | Speech only or keep all | Reject as a degraded result; do not release |
| Processing | Any | Continue bounded polling; do not submit another generation |
| Failed or canceled | Any | Stop and report the provider error or terminal state |
This is an inference from the documented response contract, implemented as application policy. `music_processed` proves that a voice source was part of the processing path; it does not prove that every word is intelligible, every source effect survived, or the mix is creatively acceptable. Human playback review remains required.
Do not replay an ambiguous generation POST
An HTTP timeout during submission does not prove that no job was created. RFC 9110 §9.2.2 says clients should not automatically retry a non-idempotent method unless they know the request semantics are idempotent or can tell the original request was not applied.
Use an application request key and durable state before the POST. If the client receives a task ID, transition to polling. If the connection fails before a task ID is stored, freeze the request as ambiguous and reconcile account or application evidence before deciding whether a new paid intent is justified. The duplicate-job guard provides the full state model.
GET polling is a different operation. Retry selected task reads on transient failures with bounded backoff and honor `Retry-After` when present. Use the async polling workflow for deadlines and terminal-state handling, and the rate-limit admission guide when several workers share one account.
Keep the bearer value, signed source URL, prompts, filenames, and temporary output capabilities out of general telemetry. The safe logging contract shows a fail-closed record shape that preserves request, task, timing, and media-shape correlation without retaining customer content.
Run the tested source-audio policy gate
`video-audio-policy-gate-v1` is a dependency-free Node.js module that converts one reviewed source-audio choice into form fields and validates a completed task before the application exposes it. The local evidence used Node.js v22.23.2 and the stable Node.js test runner.
The policy builder is deliberately small:
- `function buildPolicyFields({ sourceAudio, mixing = "static" }) {`
- ` if (sourceAudio === "replace" && mixing === "duck") {`
- ` throw new Error("Ducking requires a preserved voice source");`
- ` }`
- ` const fields = {};`
- ` if (sourceAudio === "speech-only") {`
- ` fields.preserve_speech = "true";`
- ` }`
- ` if (sourceAudio === "keep-all") {`
- ` fields.keep_original_sound = "true";`
- ` }`
- ` if (mixing === "duck") fields.ducking = "true";`
- ` return fields;`
- `}`
The acceptance side applies four independent checks to every requested variant:
- Require `succeeded` or `completed`.
- Require the exact approved variant count and a unique integer `variant_index`.
- Require `output_type="video"`, an HTTPS `output_url`, positive `output_bytes`, and nonempty `music` and `sfx` records.
- Require a nonempty `music_processed` record for `speech-only` or `keep-all`; require it to be absent for `replace`.
Run the two files together with `node --test video-audio-policy-gate.test.mjs`. Seventeen deterministic tests passed on August 25, 2026:
- Replace policy sends no preservation fields.
- Replace policy rejects meaningless ducking.
- Speech-only static policy isolates speech.
- Speech-only duck policy isolates and ducks.
- Keep-all static policy preserves the full source track.
- Keep-all duck policy preserves and ducks the full source track.
- Raw fields reject both voice-source selectors.
- Raw fields reject ducking without a voice source.
- Replace accepts a complete generated-only result.
- Keep-all accepts a complete preserved-audio result.
- `completed` is accepted as a success state.
- A voice-preserving policy rejects a generated-only fallback.
- A non-video deliverable is rejected.
- An empty video is rejected.
- An incomplete component record is rejected.
- An unexpected output count is rejected.
- Processing and failed tasks are rejected.
These tests prove the application policy against controlled JSON fixtures. They do not decode a returned video, listen for dialogue, measure loudness, inspect an invoice, test provider availability, establish quality, or guarantee that a temporary URL will remain available.
Keep the accepted file, not just its URL
An accepted response is still a handoff, not durable storage. The task reference describes output URLs as temporary. Copy the finished video into application-controlled storage, verify nonzero bytes and expected media type, record a digest, read it back, and only then mark the asset durable. The temporary-output durability guide provides that acceptance sequence.
Retain the application request key, provider task ID, exact source-audio policy, variant index, durable object ID, digest, facts date, and human reviewer decision together. Avoid storing the bearer key, signed input URL, or provider output URL after the transfer is complete.
When not to use Sonilo
Do not use this endpoint when you already have final music and sound-effects files and only need deterministic mixing. Use an editor or a tested media pipeline so you keep direct control over levels, automation, fades, and source moments.
Do not use combined generation when the original production sound is irreplaceable and a best-effort isolation path is unacceptable. Keep the source track untouched, generate separate assets, and mix them under a sound editor’s review.
Do not use one-call muxing when the deliverable needs separate music, dialogue, ambience, and effects for later revisions, localization, accessibility, or broadcaster specifications. Request or retain separate components and build the final mix downstream.
Do not treat a nonempty `music_processed` object as proof of intelligibility or creative approval. Inspect the actual video on representative speakers and headphones. Use a qualified sound editor for regulated, safety-critical, culturally sensitive, or high-value releases.
Make source audio a release requirement
The implementation decision is simple: encode one source-audio policy, send one reviewed generation request, store its task ID, poll without replaying the POST, and reject any “successful” result that lost audio your product promised to keep.
Read the current combined finished-video endpoint before integrating. If your platform needs custom capacity, workflow, or commercial terms, talk to Sonilo.


