Skip to main content

Voice messages

Voice messages use a resource-upload pipeline: request an upload URL, upload the file, verify it, wait for processing, then send its attachment_id in a message. The SDK handles the API calls; you upload the bytes directly to storage.

Storage credentials must already be configured for the tenant or chat. The current resource type is voice. See the upload endpoint for MIME types, limits and storage options.

Upload and send​

This example uses Node.js built-ins and the client from Getting started. Set chat_id when requesting the upload URL so storage is selected for the destination chat.

import { readFile } from 'node:fs/promises';
import { setTimeout as delay } from 'node:timers/promises';

const bytes = await readFile('note.mp3');
const slot = await emby.api.resourceUploadUrl({
body: {
type: 'voice',
mime: 'audio/mpeg',
name: 'note.mp3',
size: bytes.length,
chat_id: 'support-42',
},
});

if (!slot.upload_url || !slot.resource_id) {
throw new Error('The API did not return an upload URL and resource ID');
}

const upload = await fetch(slot.upload_url, {
method: slot.method,
headers: slot.headers,
body: new Uint8Array(bytes),
signal: AbortSignal.timeout(60_000),
});
if (!upload.ok) throw new Error(`Upload failed: HTTP ${upload.status}`);

const signal = AbortSignal.timeout(60_000);
let { resource } = await emby.api.resourceVerify({
path: { resource_id: slot.resource_id },
signal,
});

while (resource && resource.status !== 'ready' && resource.status !== 'failed') {
await delay(3_000, undefined, { signal });
({ resource } = await emby.api.resourceShow({
path: { resource_id: slot.resource_id },
signal,
}));
}

if (!resource || resource.status !== 'ready' || !resource.attachment_id) {
throw new Error(resource?.error ?? 'Voice processing failed');
}

await emby.sendMessage('support-42', { id: 'u-1', name: 'Alice' }, [], {
attachment_id: resource.attachment_id,
text: 'Listen to this',
});

The caption is optional when an attachment is present. Polling has a deadline because processing may remain pending if a background worker is unavailable.

Processing and failures​

A resource moves through uploaded, verifying, processing and then ready or failed. An already processed file can be ready immediately after verification; check the response before polling.

Verification checks the actual uploaded file. Missing uploads, invalid formats, expired resources and size violations can produce HTTP 422. Later decoding or conversion failures appear as failed with an error message.

The returned attachment_id can be reused in messages whose chats use the same storage bucket. The older voice_url message input is deprecated; do not pass it together with attachment_id.

See Verify an uploaded file, Resource status and Errors and retries.