jambonz Adds Speech-to-Speech Support for Azure Voice Live

jambonz

We’re happy to announce that jambonz now supports Microsoft’s Azure Voice Live API as a first-class speech-to-speech vendor. If you run jambonz v11 or later with the mediajam media engine, you can connect any phone call to a Voice Live agent with a single verb.

How Azure Voice Live Compares to Other Speech-to-Speech Vendors

Most speech-to-speech vendors give you one architecture. Voice Live gives you two, and the model name is the switch.

Name a native model such as gpt-realtime-2.1 and you get what you’d expect from a realtime API: the model hears audio and answers in its own voice.

Name a text model such as gpt-4.1, gpt-4o or gpt-5 and the same endpoint runs cascaded. Azure speech to text transcribes the caller, the text model answers, and an Azure voice speaks the reply. Microsoft manages the wiring; you still get one WebSocket and one verb. That is genuinely useful when you want a specific text model’s reasoning, or a specific brand voice, without building and operating the pipeline yourself.

On top of either mode, Voice Live layers Azure capabilities that don’t exist on the OpenAI Realtime API it otherwise resembles:

How to Add Voice Live to a jambonz Call

Here’s a minimal application using the @jambonz/sdk WebSocket interface:

session
  .answer()
  .s2s({
    vendor: 'voicelive',
    model: 'gpt-realtime-2.1',
    auth: {
      apiKey: process.env.VOICELIVE_API_KEY,
    },
    // the Voice Live endpoint is per-resource, so the host is required
    connectOptions: {
      host: 'my-resource.services.ai.azure.com',
    },
    llmOptions: {
      session_update: {
        modalities: ['text', 'audio'],
        instructions: 'You are a friendly and helpful voice assistant. ' +
          'Keep your responses concise and conversational.',
        voice: {
          name: 'en-US-Ava:DragonHDLatestNeural',
          type: 'azure-standard',
        },
        turn_detection: {
          type: 'azure_semantic_vad',
          silence_duration_ms: 500,
          remove_filler_words: true,
        },
        input_audio_noise_reduction: {
          type: 'azure_deep_noise_suppression',
        },
        input_audio_transcription: {
          model: 'azure-speech',
        },
      },
      // the agent speaks first; omit this to wait for the caller
      response_create: {
        instructions: 'Greet the caller warmly and ask how you can help.',
      },
    },
    actionHook: '/s2s-complete',
  })
  .send();

Switching that call to cascaded mode is one line: change model to gpt-4.1. The rest of the configuration, including the voice, stays exactly as it is.

Four Things Worth Knowing Before You Start

These are the things that cost us time, in the order you’re likely to hit them.

Your Azure resource has to be the right kind. Voice Live needs a Microsoft Foundry resource (kind=AIServices) whose endpoint is its own hostname, https://your-resource.services.ai.azure.com/. Strip the scheme and the trailing slash and that is your connectOptions.host.

A plain Speech resource will not do, and this is easy to miss because it looks like it should. Its only endpoint is the shared regional gateway, https://eastus.api.cognitive.microsoft.com/, which every customer in that region shares. Voice Live can’t tell which resource a request belongs to from it, so it answers 401 ... use a correct regional API endpoint for your resource. No amount of editing the URL fixes this: the per-resource hostname simply does not exist in DNS until a resource is created with one. If you need a new one:

az cognitiveservices account create \
  --name my-resource --resource-group my-group \
  --kind AIServices --sku S0 --location eastus --yes

Voice Live is fully managed, so there is no model to deploy afterwards.

The session payload is flat, not nested. Voice Live keeps voice, turn_detection and modalities at the top level of the session object. OpenAI’s GA Realtime format moved those under audio.input / audio.output. If you port a session_update across from openai_s2s unchanged, Azure won’t understand it. Note too that voice is an object ({name, type}) not a voice-id string.

Ask for caller transcripts explicitly on native models. Azure speech to text is automatic only for non-multimodal models. With gpt-realtime-2.1 you get no conversation.item.input_audio_transcription.completed events at all unless you set input_audio_transcription yourself, as in the example above. In cascaded mode it’s on by default.

Model availability is per-region. The catalog in Microsoft’s docs is not what any one resource offers. In eastus, for instance, gpt-realtime-2.1 and gpt-realtime-2.1-mini are available while plain gpt-realtime is not. An unavailable model comes back as an invalid_model error on the first session.update, which is a quick way to check what you actually have.

Where to Learn More

As always, come find us in the jambonz community with questions. We’d love to hear what you build with it.

Frequently asked questions

What is the Azure Voice Live API?

Voice Live is Microsoft's managed speech-to-speech service for voice agents. It exposes a single WebSocket that bundles speech recognition, a generative model, and text to speech, so you don't orchestrate those pieces yourself. It uses the Azure OpenAI Realtime event vocabulary but adds Azure-only capabilities such as semantic turn detection, server-side noise suppression and echo cancellation, and the full Azure TTS voice catalog.

Is this the same as jambonz's existing microsoft vendor?

No. The microsoft vendor targets the older Azure OpenAI Realtime deployment endpoint and shares its payload shapes with vendor openai. Voice Live is a separate service with its own flat session shape and its own features, so it ships as its own vendor, voicelive. A session_update written for openai_s2s will not work unchanged.

Which models can I use with Voice Live on jambonz?

Two families, and the choice changes the architecture. A native speech-to-speech model such as gpt-realtime-2.1 generates audio itself. A text model such as gpt-4.1, gpt-4o or gpt-5 runs cascaded: Azure speech to text transcribes the caller, the text model answers, and an Azure voice speaks the reply. Availability varies by region, so check what your resource actually offers.

What kind of Azure resource do I need?

A Microsoft Foundry resource, created with kind AIServices. Its endpoint looks like https://your-resource.services.ai.azure.com/ and that hostname is what jambonz connects to. A plain Speech resource will not work: its only endpoint is the shared regional gateway, which Voice Live rejects with a 401.

How do I authenticate to Voice Live from jambonz?

Put your resource key in the verb's auth.apiKey and jambonz sends it as the api-key query parameter. Alternatively, mint a Microsoft Entra ID token for the https://ai.azure.com/.default scope and pass it as auth.accessToken; jambonz sends that as an Authorization Bearer header. Entra tokens are short-lived, so mint one per call.

Does jambonz support the Voice Live avatar?

No. Voice Live's text to speech avatar requires a separate WebRTC SDP exchange with the service to carry video, which has no place in a SIP phone call. Everything else (voices, semantic VAD, noise suppression, word timestamps and viseme events) works.