CleanvoiceDocs
JavaScript SDK

JavaScript SDK

Official JavaScript and TypeScript SDK for the Cleanvoice API.

The official Cleanvoice JavaScript SDK works in Node.js and is fully typed with TypeScript. It handles authentication, polling, and provides a clean async/await interface.

Installation

npm install @cleanvoice/cleanvoice-sdk
pnpm add @cleanvoice/cleanvoice-sdk
yarn add @cleanvoice/cleanvoice-sdk
bun add @cleanvoice/cleanvoice-sdk

Initialization

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

// Recommended: reads CLEANVOICE_API_KEY from the environment
const client = Cleanvoice.fromEnv();

// Or pass the key explicitly
const client = new Cleanvoice({ apiKey: process.env.CLEANVOICE_API_KEY });

client.process()

Submit a file and wait for the result. This is the recommended method for most use cases. Blocks until the job finishes and returns a ProcessResult.

const result = await client.process(fileInput, config?, options?);

Parameters

ParameterTypeDescription
fileInputstringhttp:///https:// URL or local file path to the audio or video file
configProcessingConfigProcessing options (see below)
optionsobjectAdditional request options such as uploadType, templateId, polling, outputPath, and download

ProcessingConfig

interface ProcessingConfig {
  fillers?: boolean;               // Remove filler words ("um", "uh", etc.)
  stutters?: boolean;              // Remove repeated word fragments
  long_silences?: boolean;         // Trim long pauses
  mouth_sounds?: boolean;          // Remove clicks and lip smacks
  breath?: boolean | string;       // true, 'legacy', or 'natural'
  remove_noise?: boolean;          // Reduce background noise
  normalize?: boolean;             // Normalize loudness
  autoeq?: boolean;                // Legacy EQ option; prefer studio_sound
  studio_sound?: string | boolean; // true or advanced 'nightly'
  mute_lufs?: number;              // Enable LUFS targeting (set threshold)
  target_lufs?: number;            // Target LUFS level (e.g. -16)
  export_format?: 'auto' | 'mp3' | 'wav' | 'flac' | 'm4a'; // Audio-only; video keeps original container
  transcription?: boolean;         // Return a transcript
  summarize?: boolean;             // Generate summary and chapters (auto-enables transcription)
  social_content?: boolean;        // Generate social media copy (auto-enables summarize)
  signed_url?: string;             // Pre-signed PUT URL for your own storage
  video?: boolean;                 // Process as video; SDK auto-detects common video files
  merge?: boolean;                 // Multi-track only; merge all tracks into one output
  audio_for_edl?: boolean;         // Video workflows only; extra EDL/NLE audio output
}

social_content: true automatically enables summarize. summarize: true automatically enables transcription.

Return value — ProcessResult

interface ProcessResult {
  audio: {
    url: string;              // Remote URL for the cleaned media
    filename: string;
    localPath?: string;       // Set after download()
    statistics: {
      FILLER_SOUND?: number;
      // ...other stats
    };
    download(outputPath?: string): Promise<string>; // Save to file, returns saved path
  };
  media: {                    // Alias of audio, convenient for video output
    url: string;
    filename: string;
  };
  video: boolean;
  isVideo: boolean;
  transcript?: {
    text: string;
    paragraphs: Array<{ start: number; end: number; text: string }>;
    detailed: {
      words: Array<{ id: number; start: number; end: number; text: string }>;
      paragraphs: Array<{ id: number; start: number; end: number; speaker: string }>;
    };
    summary?: string;
    title?: string;
    chapters?: Chapter[];
    summarization?: {
      title: string;
      summary: string;
      chapters: Chapter[];
      summaries: string[];
      key_learnings: string;
      summary_of_summary: string;
      episode_description: string;
    };
  };
  summarization?: {
    title: string;
    summary: string;
    chapters: Chapter[];
    summaries: string[];
    key_learnings: string;
    summary_of_summary: string;
    episode_description: string;
  };
  socialContent:
    | []
    | {
        newsletter?: string;
        twitter_thread?: string;
        linkedin?: string;
      };
  taskId?: string;
}

Example

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

const client = Cleanvoice.fromEnv();

const result = await client.process(
  'https://example.com/episode.mp3',
  {
    fillers: true,
    long_silences: true,
    mouth_sounds: true,
    remove_noise: true,
    normalize: true,
  }
);

console.log('Cleaned audio:', result.audio.url);
console.log('Cleaned media:', result.media.url); // same value as result.audio.url
await result.audio.download('cleaned.mp3'); // save locally

Text outputs

const result = await client.process('https://example.com/episode.mp3', {
  transcription: true,
  summarize: true,
  social_content: true,
});

if (result.transcript) {
  console.log(result.transcript.text);
  console.log(result.transcript.paragraphs[0]?.text);
  console.log(result.transcript.detailed.words[0]?.text);
  console.log(result.transcript.summary);
  console.log(result.transcript.title);
  console.log(result.transcript.chapters);
}

if (result.summarization) {
  console.log(result.summarization.title);
  console.log(result.summarization.summary);
  console.log(result.summarization.chapters);
  console.log(result.summarization.key_learnings);
  console.log(result.summarization.summary_of_summary);
  console.log(result.summarization.episode_description);
}

if (!Array.isArray(result.socialContent)) {
  console.log(result.socialContent.newsletter);
  console.log(result.socialContent.twitter_thread);
  console.log(result.socialContent.linkedin);
}

client.processAndDownload()

Convenience method that calls process() and immediately downloads the result. Returns a [result, savedPath] tuple.

const [result, savedPath] = await client.processAndDownload(
  'https://example.com/episode.mp3',
  'cleaned.mp3',
  { fillers: true, normalize: true }
);

console.log('Saved to:', savedPath);

client.createEdit()

Submit a job without waiting for completion. Returns the editId for later polling.

const editId = await client.createEdit(
  'https://example.com/episode.mp3',
  {
    fillers: true,
    long_silences: true,
  }
);

console.log('Edit ID:', editId);

createEdit() also accepts an optional third argument for request options such as uploadType and templateId.

client.getEdit()

Retrieve the current status and result of a previously created edit.

const edit = await client.getEdit('edit_abc123');

console.log(edit.status); // 'PENDING' | 'PREPROCESSING' | 'CLASSIFICATION' | 'EDITING' | 'POSTPROCESSING' | 'EXPORT' | 'SUCCESS' | 'FAILURE' | 'RETRY'

if (edit.status === 'SUCCESS' && edit.result && 'download_url' in edit.result) {
  console.log('Download URL:', edit.result.download_url);
}

getEdit() returns the raw polling response shape from the API. Download helpers such as result.audio.download(...) are available on process() results, not on getEdit() responses.

Polling manually

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('Edit failed');

    await new Promise((resolve) => setTimeout(resolve, 5000));
  }
}

client.uploadFile()

Upload a local file and receive a URL you can pass to process() or createEdit().

const url = await client.uploadFile('/path/to/episode.mp3');
console.log('Uploaded:', url);

client.downloadFile()

Download a file from a URL to a local path.

const savedPath = await client.downloadFile(
  'https://example.com/cleaned.mp3',
  'output.mp3' // optional output path
);

client.checkAuth()

Verify your API key and retrieve the current public credit balances.

const account = await client.checkAuth();
console.log(account.credit.total);
console.log(account.credit.subscription);
console.log(account.credit.payg);

Error handling

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

try {
  const result = await client.process('episode.mp3', { fillers: true });
} catch (err) {
  if (err instanceof ApiError) {
    console.error('API error:', err.message, err.status);
  } else {
    throw err;
  }
}

TypeScript support

The SDK ships with full TypeScript definitions. All config options and response types are exported:

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

const config: ProcessingConfig = {
  fillers: true,
  normalize: true,
  export_format: 'wav',
};

const result: ProcessResult = await client.process(url, config);

Supported formats

TypeFormats
Audio (local uploads).wav, .mp3, .flac, .m4a
Video.mp4, .mov, .webm, .avi, .mkv

Remote inputs should use http:// or https:// URLs.

Usage in Next.js

// app/api/clean/route.ts
import { NextResponse } from 'next/server';
import { Cleanvoice } from '@cleanvoice/cleanvoice-sdk';

const client = Cleanvoice.fromEnv();

export async function POST(req: Request) {
  const { url } = await req.json();

  const editId = await client.createEdit(url, {
    fillers: true,
    long_silences: true,
  });

  return NextResponse.json({ editId });
}

Never expose your API key in client-side code (browser bundles, React components without server boundaries, etc.). Always call Cleanvoice from server-side code only.