@xconvert/sdk · TypeScript

Node 18+ · CJS · ships types.

Install

Shell
npm install @xconvert/sdk

End-to-end: upload a file and convert it

This is the canonical flow. Every other example in this site is a slice of this:

TypeScript
import { Xconvert } from '@xconvert/sdk/v1';
import * as fs from 'node:fs/promises';

const xc = new Xconvert({ apiKey: process.env.XCONVERT_API_KEY! });

// 1. Read the file you want to convert.
const bytes = await fs.readFile('song.mp3');

// 2. Create an upload session.
const upload = await xc.uploads.createUpload({
    requestBody: {
        featureId: 'convert-mp3-to-aac',
        files: [{ name: 'song.mp3', sizeBytes: bytes.length, contentType: 'audio/mpeg' }],
    },
});
const handoff = upload.uploads[0];

// 3. PUT the bytes to the edge chunk endpoint.
//    Small files: one chunk. Larger files: split + repeat with stepped Content-Range.
const putRes = await fetch(`${handoff.chunkEndpoint}?totalChunks=1`, {
    method: 'PUT',
    headers: {
        'Content-Range': `bytes 0-${bytes.length - 1}/${bytes.length}`,
        'Content-Type': 'application/octet-stream',
    },
    body: new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength),
});
if (!putRes.ok) throw new Error('upload chunk failed');

const done = await xc.uploads.completeUpload({ uploadId: upload.uploadId });
const fileId = done.files[0].fileId;

// 4. Submit the conversion. \`options: {}\` is fine — the server fills in
//    baselines + defaults from the schema.
const job = await xc.submitJob({
    featureId: 'convert-mp3-to-aac',
    fileIds: [fileId],
    fromType: 'mp3',
    toType: 'aac',
    processType: 'convert',
    options: {},
});

// 5. Poll until terminal.
const final = await xc.pollUntilDone(job.id);
console.log(final.outputs?.[0]?.downloadUrl);

Configuration

TypeScript
new Xconvert({
    apiKey: 'xck_live_...',                       // required
    baseUrl: 'https://api.staging.xconvert.com',  // optional; default api.xconvert.com
    headers: { 'X-Request-Id': '...' },           // optional
});

What's on the client

Typed service handles for each API group, plus three shortcuts:

SurfaceNotes
xc.jobs.*createJob, getJob, listJobs, cancelJob
xc.uploads.*createUpload, completeUpload, cancelUpload
xc.apiKeys.*list/create/get/revoke/rotate
xc.webhooks.*list/create/patch/stop/resume + rotateSecret
xc.features.*validateFeatureOptions
xc.me.*getMe — current caller introspection
xc.billing.*overview, usage, history
xc.submitJob(req, idempotencyKey?)shortcut for jobs.createJob
xc.getJob(id)shortcut for jobs.getJob
xc.pollUntilDone(id, {intervalMs, timeoutMs})polls jobs.getJob until terminal

Common operations

List your jobs

TypeScript
const page = await xc.jobs.listJobs({ limit: 20, status: 'complete' });

Cancel a queued job

TypeScript
await xc.jobs.cancelJob({ id: job.id });

Caller introspection (auth + credit balance)

TypeScript
const me = await xc.me.getMe();
console.log(me.apiTier, me.credits.payg_remaining);

Webhook endpoint

TypeScript
const endpoint = await xc.webhooks.createWebhookEndpoint({
    requestBody: {
        url: 'https://yoursite.com/hooks/xconvert',
        events: ['job.completed', 'job.failed'],
    },
});
console.log(endpoint.secret);   // shown ONCE — sign incoming webhook payloads with it

Error handling

Service methods throw ApiError (re-exported from the generated client). The body follows RFC 9457 Problem Details:

TypeScript
import { ApiError } from '@xconvert/sdk/v1';

try {
    await xc.submitJob({ ... });
} catch (e) {
    if (e instanceof ApiError) {
        console.log(e.status, e.body); // body.code, body.detail, body.extensions
    } else { throw e; }
}

Subpath versioning

TypeScript
import { Xconvert } from '@xconvert/sdk/v1';    // current
// import { Xconvert as XconvertV2 } from '@xconvert/sdk/v2'; // when it ships