CleanvoiceDocs
JavaScript SDK

Recommendations

Recommended patterns for high-volume and production audio processing with the JavaScript SDK.

Choosing the right breath mode

ValueWhen to use
trueRecommended for most recordings. Best default for noisy or challenging audio.
'legacy'Source audio is already clean and well-recorded. Conservative, predictable removal.
'natural'You want a lighter touch that preserves more of the speaker's natural breathing.
falseLeave breathing untouched (default).

Choosing the right studio_sound mode

ValueWhen to use
trueRecommended. Aggressive enhancement for studio-quality output.
'nightly'Advanced/experimental variant. Currently behaves similarly to true.
falseDisabled (default). Use remove_noise alone for lighter cleanup.

autoeq is legacy and will be removed in a future release. Use studio_sound instead.


Preserve original timing with muted

If you need the output to keep the exact same duration as the input — for example, to stay in sync with a video timeline or subtitle file — set muted: true. Edits are silenced instead of cut, so no content shifts in time.


Video: send audio only when you're enhancing, not editing

When you want to edit a video — remove filler words, cut silences, trim stutters — you must send the video file directly. Cleanvoice applies cuts to the video timeline and returns a shortened video with the edits applied.

In the JavaScript SDK, common video filenames and URLs are auto-detected, but explicit video: true is still safest for ambiguous or extensionless URLs.

When you only want to enhance audio (noise reduction, studio sound, normalization) without any cuts, the video stream is irrelevant. Extract the audio, process the small file, then mux the cleaned audio back. The upload can be 10–50× smaller and you get the same result.

Job typeWhat to send
Fillers, silences, stutters, mouth sounds, breathSend the video file directly
Noise reduction, studio sound, normalization onlyExtract audio → process → mux back

Enhancement-only pattern:

# 1. Extract audio (no re-encode, no quality loss)
ffmpeg -i input.mp4 -vn -acodec copy audio.aac

# 2. Send audio.aac to Cleanvoice, get back cleaned.aac

# 3. Mux cleaned audio back into the original video container
ffmpeg -i input.mp4 -i cleaned.aac -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 output.mp4

In Node.js:

import { execSync } from 'child_process';
import { Cleanvoice } from '@cleanvoice/cleanvoice-sdk';

const client = Cleanvoice.fromEnv();

// Step 1: extract audio
execSync('ffmpeg -i input.mp4 -vn -acodec copy audio.aac -y');

// Step 2: enhance audio only (no editing/cuts)
const result = await client.process('audio.aac', {
  remove_noise: true,
  studio_sound: true,
  normalize: true,
});
await result.audio.download('cleaned.aac');

// Step 3: mux back — video is untouched, only audio is replaced
execSync(
  'ffmpeg -i input.mp4 -i cleaned.aac -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 output.mp4 -y'
);

-acodec copy extracts the audio stream without re-encoding it. -c:v copy -c:a copy in the mux step remuxes without transcoding. No quality loss at either step.


Deliver results directly to your storage

By default, Cleanvoice hosts the cleaned file and you download it from our servers. At scale, this means every file travels the full round trip: your servers → Cleanvoice → your servers.

Use signed_url to eliminate the return leg. Generate a pre-signed PUT URL for your S3/GCS/Azure bucket before submitting the job. Cleanvoice will PUT the cleaned file directly into your storage — you never need to download it.

import { S3Client, PutObjectCommand, getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { Cleanvoice } from '@cleanvoice/cleanvoice-sdk';

const client = Cleanvoice.fromEnv();
const s3 = new S3Client({ region: 'eu-central-1' });

// Generate a pre-signed PUT URL (valid for 1 hour)
const signedUrl = await getSignedUrl(
  s3,
  new PutObjectCommand({ Bucket: 'my-bucket', Key: 'cleaned/episode.mp3' }),
  { expiresIn: 3600 }
);

const result = await client.process('audio.aac', {
  fillers: true,
  normalize: true,
  signed_url: signedUrl,
});
// The cleaned file is now at s3://my-bucket/cleaned/episode.mp3
// No download needed

The signed_url must be a PUT URL, not a GET URL. Most S3-compatible storage providers support pre-signed PUT URLs. The URL must remain valid until Cleanvoice finishes processing — use at least a 1-hour expiry for long files.


Upload once, reuse across requests

If you need to process the same file multiple times with different settings (or retry a failed job), upload it once and reuse the URL. The uploaded URL is not public — it's a private Cleanvoice storage URL only accessible by your API key.

// Upload once
const fileUrl = await client.uploadFile('episode.mp3');
// → "https://storage.cleanvoice.ai/uploads/your-key/..."

// Reuse for multiple jobs — no re-upload needed
const [id1, id2, id3] = await Promise.all([
  client.createEdit(fileUrl, { fillers: true, normalize: true }),
  client.createEdit(fileUrl, { transcription: true }),
  client.createEdit(fileUrl, { remove_noise: true, studio_sound: true }),
]);

Uploaded files are retained for 7 days, then deleted automatically. You can also delete them early with DELETE /v2/edits/{edit_id}.


Batch processing

Submit all jobs first, then poll — never wait serially between files.

import { Cleanvoice } from '@cleanvoice/cleanvoice-sdk';

const client = Cleanvoice.fromEnv();

const files = ['ep1.mp3', 'ep2.mp3', 'ep3.mp3'];

// Submit all jobs immediately
const editIds = await Promise.all(
  files.map((f) => client.createEdit(f, { fillers: true, normalize: true }))
);

// Poll for completion
async function waitForEdit(editId: string) {
  while (true) {
    const edit = await client.getEdit(editId);
    if (edit.status === 'SUCCESS') return edit;
    if (edit.status === 'FAILURE') throw new Error(`Failed: ${editId}`);
    await new Promise((r) => setTimeout(r, 10_000));
  }
}

const results = await Promise.allSettled(editIds.map(waitForEdit));

Do not pass multiple files in one process() call to simulate batching — that activates multi-track mode (for interviews recorded on separate mics). Submit one job per file.