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.

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.
| Item | MAI voice | Sume voice |
|---|---|---|
| Example | en-US-Harper:MAI-Voice-2.1-Flash | a UUID, or voi_ plus 32 hex |
| Model chosen by | Suffix after the colon | The route, or the router's model field |
| Bad value | Not stated here | 400 invalid_voice_id, no job, no reservation |
| Alternative selector | None on the page | avatar_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
- Make a Talking Photo Speak Spanish: TTS Language, Then Fabric
Two steps on Sume: generate Spanish speech with the language set, then animate a still with Fabric. Costs for a 30-second clip and what Avatar Video can't do.
- Map a bulk queue item index back to a SKU: keep your own ledger
Queue items return index, status and run_id, not your SKU. Save a SKU-by-index table at submit time and join it to the queue receipt when you poll.
- Marketing API v24.0 ends Oct 6, 2026: pin the version in your uploader
Meta lists Marketing API v24.0 as available until October 6, 2026 and v25.0 as latest. Make the version a setting, and keep Sume render jobs separate.
- Marketing API v24 expiry runbook: replay the upload, not the render
When Meta's v24.0 window closes on October 6, 2026, fix uploads by replaying stored Sume job results. The same Idempotency-Key never bills a render twice.
Written by Sume