MAI voice ids end in a model name; Sume voice ids do not

A MAI id like en-US-Harper:MAI-Voice-2.1-Flash is not a Sume voice id. Sume takes a UUID or voi_ plus 32 hex and returns 400 invalid_voice_id for anything else.

4 min readSume
All posts

Microsoft's MAI voice ids carry a model suffix after a colon, for example en-US-Harper:MAI-Voice-2.1-Flash, so one name picks both the voice and the model. Sume voice ids look nothing like that: voice.id must be a TTS voice UUID or a library id made of voi_ plus 32 hex characters, and anything else is rejected with 400 invalid_voice_id before a job is queued or credits are reserved.

Where the suffix comes from

The Microsoft voices page, updated 2026-10-01, shows the voice name, a colon and the model, and notes the models are public preview with no SLA and not recommended for production. The locale prefix, such as en-US, belongs to the name; the part after the colon selects the model.

Sume splits those two decisions. The voice comes from voice.id, avatar_id or avatar_handle, and the model comes from the route: POST /v1/tts-1.0/generate has no engine picker and rejects model and model_id, while the router route takes an explicit catalog model.

Identifier shapes (Microsoft voices page read 2026-10-05; Sume from the repo)
ItemMAI voiceSume voice
Exampleen-US-Harper:MAI-Voice-2.1-Flasha UUID, or voi_ plus 32 hex
Model chosen bySuffix after the colonThe route, or the router's model field
Bad valueNot stated here400 invalid_voice_id, no job, no reservation
Alternative selectorNone on the pageavatar_id or avatar_handle

What to do when you migrate

Do not try to map names. A MAI voice name has no Sume counterpart, so build a table once: audition candidates, store the Sume id you pick next to your own voice label, and send that id. If both avatar_id and voice.id are given they must match, so pick one selector per request.

Limits: this concerns identifiers only. It says nothing about how a voice sounds, and Sume has no voice-cloning endpoint, so you cannot recreate a MAI voice there. Listen before you replace one.

Why the early rejection matters

Because the check runs synchronously, a wrong id costs you one round trip rather than a queued job. Nothing is reserved against your balance and nothing is stored, so a loop that pastes vendor names fails fast and cleanly on the first line.

That is useful for a migration script. Run the first request of a batch alone, confirm it is accepted, and only then fan out. The failure message tells you the field, and the fix is always the same: copy the id verbatim from the Voices library, or send an avatar selector and let Sume resolve the voice.

A pre-flight check for ids

Validate the id shape in your own code before any request, so a pasted vendor name never reaches the API. The check below is cheap and covers both accepted shapes.

import re

UUID = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.I)
LIB = re.compile(r"^voi_[0-9a-f]{32}$", re.I)

def sume_voice_ok(v):
    return bool(UUID.match(v) or LIB.match(v))

print(sume_voice_ok("en-US-Harper:MAI-Voice-2.1-Flash"))
print(sume_voice_ok("voi_" + "a" * 32))

Sources

Related posts

More in Developers

All Developers posts

Written by Sume