Authenticate to generate an API key and manage credits for the B2B mastering API.
Sign in to generate API keyPre-paid credit “blocks” make budgeting simple. Buy the bundle that fits your volume—no setup fees or hidden charges.
| Package | Masters included | Effective rate | Up-front price | Checkout |
|---|---|---|---|---|
| Starter | 500 (500 credits) | $0.50 | $250.00 | |
| Growth | 2,500 (2,500 credits) | $0.25 | $625.00 | |
| Pro Studio | 5,000 (5,000 credits) | $0.20 | $1,000.00 | |
| Production House | 10,000 (10,000 credits) | $0.17 | $1,700.00 | |
| Enterprise | 30,000 (30,000 credits) | $0.14 | $4,200.00 | |
| Custom SLA | 50k+ | Negotiated | Custom | Talk to sales |
Authenticate requests with your API key. Each song submission costs one credit. Credit rates start at $0.50 and decrease to $0.14 at higher tiers. Mastered files stay on our CloudFront CDN for 30 days—download anything you need to keep beyond that window.
Ready‑to‑run Next.js demo
Clone, add your API key & CloudFront domain, and run locally. Implements upload → submit → poll → CloudFront playback with intensity ladder.
Open GitHub demoSign in to see your API key automatically inserted into every example.
Mastered files are delivered via our CloudFront CDN and remain available for 30 days. Make sure to download any files you need to keep beyond that period.
Authenticate requests with your API key. Each song submission costs one credit. Credit rates start at $0.50 and decrease to $0.14 at higher tiers. Mastered files stay on our CloudFront CDN for 30 days—download anything you need to keep beyond that window.
Create a .env.local file in the project root before running the demo. The variables below mirror the production configuration so you can make authenticated requests against the live API.
CM_API_KEY=your-live-or-sandbox-key
PARENT_BASE_URL=https://chosenmasters.com
NEXT_PUBLIC_MASTERING_CLOUDFRONT_URL=https://d2ojxa09qsr6gy.cloudfront.netKeep CM_API_KEY private—expose it only in server-side routes or scripts that proxy requests. The NEXT_PUBLIC_* variables are safe to surface in the browser and power the waveform previews inside this documentation site.
POST /api/b2b/mastering with that s3Key to enqueue mastering. This deducts one mastering credit immediately and stamps the job with type: "b2b", bypassing the retail purchase verification flow.GET /api/b2b/mastering/:id or GET /api/b2b/mastering/:id/audio until the mastered assets are ready. If the engine returns 202 Accepted, begin audio polling while CloudFront warms the signed URLs.Expect 401 for missing or invalid API keys and 403 when a tenant runs out of credits. Any other non-2xx response indicates the request never reached the mastering engine and should be retried after addressing the returned error message.
The mastering engine exposes three tonal profiles. You must include one of these values in the mode field whenever you enqueue a job—the API will reject requests that omit it.
process – Modern sheen and width. This is the default used by the demo app and mirrors the retail experience.lite – Open, gentle lift that preserves additional transient detail.warm – Powerful, saturated tilt for productions that need extra weight.The playground form and accompanying API route already forward the selected mode so you can mirror the same field in your own integration.
If you are integrating from a fresh Node.js project, install the helper dependencies and create a component like the one in the complete example below.
npm install axios react-dropzoneRequest a signed URL from POST /api/b2b/mastering/upload-url using your API key in the x-api-key header. The signed URL response includes:
uploadUrl – the pre-signed S3 destination for your file.s3Key – send this in Step 2 to trigger mastering.headers – required headers for the direct S3 upload.expiresIn – seconds before the upload URL becomes invalid.Use the exact headers returned to complete the upload before the expiry window closes.
const API_KEY = "YOUR_API_KEY_HERE";
const response = await fetch('https://chosenmasters.com/api/b2b/mastering/upload-url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': API_KEY,
},
body: JSON.stringify({
fileName: 'mix.wav',
fileType: 'audio/wav',
}),
});
if (!response.ok) {
throw new Error('Unable to request upload URL');
}
const { uploadUrl, s3Key, headers, expiresIn } = await response.json();
console.log('Upload using signed URL before it expires:', expiresIn, 'seconds');
// PUT your file to uploadUrl with the provided headers before expiresIn seconds elapse.Once your file is safely stored, call POST /api/b2b/mastering with the s3Key from the upload step. Choose a processing mode (defaults to process) and optionally override the title. The platform deducts one credit as soon as the job is accepted and automatically flags it with type: "b2b". A 403 response indicates the tenant is out of credits.
const API_KEY = "YOUR_API_KEY_HERE";
const res = await fetch('https://chosenmasters.com/api/b2b/mastering', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': API_KEY,
},
body: JSON.stringify({
s3Key: 'path/to/file.wav',
title: 'My Track',
ext: 'wav',
size: 5.4, // number (MB)
mode: 'process', // process | lite | warm
}),
});
if (!res.ok) {
throw new Error('Mastering submission failed');
}
const data = await res.json();
// Most responses are 202 Accepted for async rendering:
console.log('Mastering job queued with ID:', data.jobId);
if (data.expectedKey) console.log('Deliverable key:', data.expectedKey);
if (data.expectedUrl) console.log('CloudFront URL:', data.expectedUrl);Optional mode values: process (Modern), lite (Open), or warm (Powerful). Defaults to process.
Poll the job endpoint until mastered is true. Every response returns expectedKey, expectedUrl, and a deliverables array so you can pre-compute CloudFront download paths while the render completes.
Once mastered is true, the payload includes a signed CloudFront URL for instant playback. If any deliverable is unavailable, continue polling—the job automatically refunds its credit if rendering fails.
const API_KEY = "YOUR_API_KEY_HERE";
const res = await fetch('https://chosenmasters.com/api/b2b/mastering/' + jobId, {
headers: { 'x-api-key': API_KEY },
});
if (!res.ok) {
throw new Error('Unable to retrieve mastering job');
}
const data = await res.json();
console.log('Mastering job status:', data);
if (data.expectedKey) console.log('Deliverable key:', data.expectedKey);
if (data.expectedUrl) console.log('CloudFront URL:', data.expectedUrl);
if (Array.isArray(data.deliverables)) console.log('All deliverables:', data.deliverables);
if (data.mastered && data.url) {
console.log('Download your mastered file at:', data.url);
// Stream while the signed CloudFront URL is valid:
// document.getElementById('mastered-preview').src = data.url;
}After the job reports mastered, call GET /api/b2b/mastering/:id/audio to retrieve CloudFront URLs for each intensity. Don’t stop when Level 3 arrives—keep polling until all requestedLevels are ready, or until ~30 seconds after the first playable CloudFront URL appears (grace window). This mirrors the production player and avoids cutting the ladder short while CDN links are still warming.
intensities[] — array of intensity levels with level, available, url, expiresAt, and type.availableLevels[] — mastered intensities ready to play. Clamp UI sliders to these values.requestedLevels[] — all intensities requested during submission; use this to determine when “all ready” has been reached.originalUrl — CloudFront playback path for the original upload (available even while masters warm).Signed URLs may take a few seconds to warm. The helper below continues polling beyond Level 3 and stops once all requested levels are available or the grace window expires.
const API_KEY = "YOUR_API_KEY_HERE";
/**
* Poll intensities until:
* 1) ALL requestedLevels are available, OR
* 2) ~30s after the first playable CloudFront URL appears (grace window).
*/
async function pollMasteredIntensities(jobId, { pollMs = 3000, graceMs = 30000 } = {}) {
let firstPlayableAt = null;
let requested = null;
// Utility sleep
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
while (true) {
const res = await fetch(
'https://chosenmasters.com/api/b2b/mastering/' + jobId + '/audio?intensity=all',
{
mode: 'cors',
headers: { 'x-api-key': API_KEY },
}
);
if (!res.ok) {
throw new Error('Unable to load mastered intensities');
}
const data = await res.json();
// Establish requestedLevels once (fallback to 1..5 if not present)
if (!requested) {
requested = Array.isArray(data.requestedLevels) && data.requestedLevels.length
? data.requestedLevels.slice()
: [1, 2, 3, 4, 5];
}
const intensities = Array.isArray(data.intensities) ? data.intensities : [];
const playable = intensities.filter((i) => i.available && i.url);
const ready = new Set(playable.map((i) => i.level));
// Mark when CloudFront first becomes playable
if (playable.length && !firstPlayableAt) {
firstPlayableAt = Date.now();
}
// Stop if all requested levels are ready
const allReady = requested.every((lvl) => ready.has(lvl));
if (allReady) {
return data; // all requested intensities are available
}
// Or stop after grace window once CF is first playable
if (firstPlayableAt && Date.now() - firstPlayableAt >= graceMs) {
return data; // good enough for UI; others will warm shortly
}
await wait(pollMs);
}
}
// Usage:
const result = await pollMasteredIntensities(jobId);
console.log('Original file:', result.originalUrl);
console.log('Requested levels:', result.requestedLevels);
console.log('Available levels:', result.availableLevels);
console.log('All intensities:', result.intensities);
const preferred =
(result.intensities || []).find((i) => i.level === 3 && i.available && i.url) ||
(result.intensities || []).find((i) => i.available && i.url);
if (!preferred) {
console.log('Mastered previews still warming. Keep polling until ready.');
} else {
console.log('Play preferred master at:', preferred.url);
}
function pickMp3Preview(intensities) {
if (!Array.isArray(intensities)) return null;
const mp3 = intensities.find(
(item) =>
item &&
item.available &&
typeof item.url === 'string' &&
/.mp3(?:?|$)/i.test(item.url)
);
if (mp3) return mp3.url;
const fallback = intensities.find(
(item) => item && item.available && typeof item.url === 'string'
);
return fallback ? fallback.url : null;
}
function swapExtension(url, nextExt = 'wav') {
if (!url || !nextExt) return url;
const [path, query] = url.split('?');
if (!/.(mp3|wav|m4a|flac)$/i.test(path)) return url;
const updated = path.replace(
/.(mp3|wav|m4a|flac)$/i,
'.' + nextExt.toLowerCase()
);
return query ? updated + '?' + query : updated;
}
const previewUrl = pickMp3Preview(result.intensities);
const wavDownloadUrl = swapExtension(previewUrl, 'wav');
const mp3DownloadUrl = swapExtension(previewUrl, 'mp3');
Always feed your audio player a CloudFront-signed .mp3 preview. The CDN warms MP3 first, so waiting for another format can stall playback even though the mastered ladder is already ready to audition.
The helper above mirrors the landing page implementation: prefer the MP3 preview for streaming, then swap extensions on the same signed path when you need a .wav or alternate download without launching a second mastering pass.
The demo download button mirrors the currently selected intensity. Use the format picker to toggle the CloudFront link between the mastered preview format and a .wav render—the URL simply swaps extensions before the signed query string so you can grab either asset without another mastering pass.