postMessage API
Open audiocutter.online/import/ as a popup — or embed it in an <iframe> — from your website and hand off an audio file to the editor via window.postMessage. The user lands in the editor with the file already loaded — no upload step, no roundtrip to your server.
This is the lightest integration path. You can also get the edited audio back into the same window — no server, no callback URL. If you need a pre-filled multi-fragment project, branded UI, or a server-side submit-back callback, use Hosted Sessions instead — see Which method should I use?.
Quick start
// 1. Open the import page as a popup
const popup = window.open('https://audiocutter.online/import/')
// 2. Listen for the ready signal
window.addEventListener('message', (event) => {
if (event.data?.source === 'audiocutter' && event.data.type === 'ready') {
// 3. Send the audio file
popup.postMessage({
type: 'audio',
file: file // File object
}, 'https://audiocutter.online')
}
})That's the entire contract. After the file is received, the popup stores it in IndexedDB and navigates to /, where the editor opens with the file loaded.
Message protocol
Editor → host: ready signal
Once the import page loads, it posts a single ready signal to its host — window.opener for a popup or window.parent for an iframe — with target origin *:
{ source: 'audiocutter', type: 'ready' }Always check both event.data.source === 'audiocutter' and event.data.type === 'ready' before responding — your listener will see every postMessage the page receives.
Host → editor: audio file
Send back a single message containing the audio file:
{
type: 'audio',
file: File // a File or File-like Blob from <input type="file"> or drag-and-drop
}Always specify the target origin as 'https://audiocutter.online' on the postMessage call. Using '*' would leak the file to whatever origin happens to occupy the popup if the user navigates away mid-handshake.
Only one audio file per session. To replace it, send another message — the popup overwrites the IndexedDB entry and redirects again.
Legacy alias
For backwards compatibility, the popup also accepts { type: 'audio', audio: Blob } (the field is audio, the value is a Blob instead of a File). It is wrapped into a File named imported-audio. New integrations should use the file field with a real File object so the filename and MIME type are preserved.
Getting the edited audio back
The single-file handoff above is one-way. To receive the edited audio back in the same window — no server, no callback URL — send a session message instead of an audio message, with an output of mode: 'postmessage'. When the user clicks Submit, the editor renders the audio and posts it straight back to your host window (window.opener or window.parent):
window.addEventListener('message', (event) => {
if (event.data?.source !== 'audiocutter') return
if (event.data.type === 'ready') {
popup.postMessage({
type: 'session',
manifest: {
version: 2,
project: {
files: [{ id: 'clip', file: file }], // File object
fragments: [{ id: 'f1', fileId: 'clip' }],
},
session: {
// Post the rendered result back here instead of to a server.
output: { mode: 'postmessage', format: 'mp3', targetOrigin: location.origin },
},
},
}, 'https://audiocutter.online')
}
// Arrives when the user clicks Submit in the editor.
if (event.data.type === 'result') {
const { audio, filename, mimeType, manifest } = event.data
// audio is a Blob — save it, upload it, or play it.
const url = URL.createObjectURL(audio)
}
})The result message has this shape:
{
source: 'audiocutter',
type: 'result',
audio: Blob, // rendered audio in the chosen format
filename: string, // e.g. 'session.mp3'
extension: string, // e.g. 'mp3'
mimeType: string, // e.g. 'audio/mpeg'
manifest: object // the edited project manifest (same schema)
}output.targetOrigin is optional (defaults to '*') but recommended — set it to your origin so only your page can receive the audio. The full manifest shape (multiple files, fragments, volumes, fades, branding) is documented under Hosted Sessions; mode: 'postmessage' is just one of its output modes — the only one that needs no server.
Timeout
The popup waits 30 seconds for the audio message. If nothing arrives in that window, it falls back to showing the integration guide as a static page. Send the file promptly after receiving ready.
Popup blockers
Browsers only allow window.open from a user gesture (click, keypress, etc.). Call it directly inside the event handler — do not wait for an async operation first:
// ❌ Popup blocked — async work runs first
button.addEventListener('click', async () => {
await fetch('/api/draft') // gesture is lost here
window.open('https://audiocutter.online/import/')
})
// ✅ Open synchronously, then do async work
button.addEventListener('click', () => {
const popup = window.open('https://audiocutter.online/import/')
fetchAndSend(popup)
})Embed inline (iframe)
Prefer to keep the user on your page instead of opening a popup? Embed /import/ in an <iframe>. The protocol is identical — the only difference is that the editor talks to window.parent instead of window.opener. The import page detects whichever host exists, so the same messages work in both modes; you post to the frame instead of the popup:
const iframe = document.querySelector('iframe') // src="https://audiocutter.online/import/"
window.addEventListener('message', (event) => {
if (event.origin !== 'https://audiocutter.online') return
if (event.data?.source !== 'audiocutter') return
if (event.data.type === 'ready') {
iframe.contentWindow.postMessage({
type: 'audio',
file: file // File object
}, 'https://audiocutter.online')
}
// 'result' / 'error' arrive here exactly as in the popup flow.
})A few things to keep in mind when embedding:
- If you set the
sandboxattribute, it must includeallow-scriptsandallow-same-origin(the editor needs same-origin storage for the file handoff). Addallow-downloadsif users export inside the frame, andallow-popupsif you also keep a popup fallback. - The editor needs room — give the iframe a generous width/height (it's a full waveform editor, not a widget).
- Browsers partition a third-party iframe's storage by your top-level site. The
/import/ → /handoff stays inside one partition so it works, but test your target browsers (notably Safari) before relying on it in production.
Headers on your side
Nothing special needed. Open the popup (or embed the iframe) and call postMessage over plain HTTPS — no extra headers on your site. The host reference (window.opener for a popup, window.parent for an iframe) stays available for the whole session, so the editor can message you back too.
Which method should I use?
| Need | Use |
|---|---|
| Drop one audio file into the editor for the user to edit. | postMessage API (this page) |
| Pre-fill a multi-fragment project, set volumes/fades/compression/filter. | Hosted Sessions |
| Brand the editor with a partner logo / accent color / banner. | Hosted Sessions |
| Receive the edited audio back in the browser, no server. | postMessage API (this page) |
| Receive the edited audio back at a server callback URL. | Hosted Sessions |
| AI pipeline / partner workflow with HITL refinement step. | Hosted Sessions |
When in doubt: if your host page stays open and can receive the result directly, the postMessage API covers the whole round-trip. Reach for hosted sessions when you need a server-side callback, a project loaded from a URL, or partner branding.
Questions?
Have a question or need help with the integration? and we’ll get back to you.