beatraDocs
Speech & voices

AI Director

Let the AI Director read the text and direct the performance before synthesis, so the voice sounds like a real person.

Let the AI Director read the text and direct the performance before synthesis, so the voice sounds like a real person.

The AI Director reads the text and the user's requirements, then returns a script that is ready to synthesize. It adds emotion, performance, sound effects, pauses and pronunciation in the form the target voice can actually perform. It only adds direction: it never changes a single word of the text. Without a voice, it also recommends a suitable voice and model and explains why.

Primary agent path

Use the Universal Skill with the MCP tool beatra.speech.prepare_script. The tool is billable and returns an asynchronous task.

  1. Put the text in input and the user's wishes for the result, if any, in instructions. Pass the voice or model when the user named one; otherwise omit them and let the AI Director recommend.
  2. Create one opaque client_request_id, submit once, and poll with beatra.tasks.get until the task is terminal.
  3. Call beatra.speech.synthesize with each returned script's synthesis as the arguments, unchanged, plus your own client_request_id. A script whose synthesis is null cannot be synthesized as is; its warnings say why.
  4. When the user wants a change ("more tension in the second paragraph"), add that sentence to instructions (keep the earlier requirements), call again, then synthesize again.

AI guide

MCP integration guide for AI

For custom API integrations, copy this guide into Claude, Codex, or another coding assistant.

Tool arguments

{
  "input": "The night was still. He pushed the door open. \"Is anyone there?\" No one answered.",
  "instructions": "A little suspenseful; whisper the question.",
  "client_request_id": "script-opaque-1"
}
  • input: the text, in any supported language, up to 50,000 characters. Markup already in the text (such as {pause 0.5}) is kept as written; the AI Director only adds to it. When the text is longer than the voice or model you named takes in one call (10,000 characters for ElevenLabs), the call is refused with the same 422 error as synthesis, and nothing is charged.
  • instructions: optional requirements in natural language, in any language, up to 2,000 characters. When given, they come first, and whatever they do not mention is performed naturally by the meaning of the text; without them, the whole text is.
  • voice, model: optional, with the same meaning and defaults as in beatra.speech.synthesize. With neither, the AI Director recommends both. With a voice only, it picks among that voice's models. With a model only, it recommends a voice available on that model.
  • language: optional; usually omit it (auto, the default, detects the language). Send it only when the user names the language or a dialect the text cannot show, such as Cantonese (yue).
  • context: optional and advanced. When a long text is directed in parts, pass the text around the part as {"before": "…", "after": "…"}, each up to 2,000 characters. It is used only to understand the tone and is never annotated, read aloud or returned. When the target model supports it, the end of before is suggested as synthesis.previous_text so the tone carries over.

Result

When the task succeeds, output holds a list of scripts:

{
  "type": "speech_script",
  "model": "ai-director",
  "directed": true,
  "scripts": [
    {
      "synthesis": {
        "input": "{emotion calm}The night was still. He pushed the door open. {emotion fearful}{whisper}\"Is anyone there?\" No one answered.",
        "voice": "voice_01JX...",
        "model": "elevenlabs-v4-turbo",
        "language": "en"
      },
      "summary": "A tense opening with a whispered question",
      "voice_selection": {
        "source": "recommended",
        "voice": "voice_01JX...",
        "model": "elevenlabs-v4-turbo",
        "name": "Arthur",
        "preview_url": "https://cdn.beatra.ai/voices/previews/voice_01JX....mp3",
        "reason": "A mature male voice for narration that performs line by line."
      },
      "alternatives": [
        {"voice": "voice_01JY...", "model": "minimax-speech-2.8-hd", "name": "Barb",
         "preview_url": "https://cdn.beatra.ai/voices/previews/voice_01JY....mp3", "reason": "A young female voice for audiobooks."}
      ],
      "estimate": {"weighted_characters": 96, "price_credits": "24"},
      "warnings": []
    }
  ],
  "warnings": []
}
  • scripts: one synthesize call per script. Today there is always exactly one; later there can be several (for example one per speaker), so handle each.
  • synthesis: the arguments for synthesizing this script; pass it unchanged to beatra.speech.synthesize (plus client_request_id). input is the script with its markup; then come voice, model, language, and the whole-script controls the target voice actually follows: emotion, direction, previous_text (a control it does not follow is left out). It is null when the script cannot be synthesized as is: there is no usable voice, the voice cannot read the script, or the validation could not run this time (retry later); the warnings say which.
  • summary: a one-line description of the performance that you can tell the user as is, free of parameter names and markup. It is in the language of instructions, or of the text when there are no instructions.
  • voice_selection: the voice of this script, with voice, model, the voice's name and a preview_url to listen to. source is recommended when the AI Director chose it, with the reason; it is given for the voice you passed, with reason null. The whole field is null when there is no voice.
  • alternatives: when the voice was recommended, up to 3 other voices with the same fields as voice_selection (without source); empty when you passed the voice.
  • estimate: the exact price of synthesizing this script, the same as the free validation; it is null whenever synthesis is.
  • directed: false means the AI Director could not direct the text this time. The script is the original text and the direction is not charged; when it has a synthesis, it can still be synthesized directly.
  • When no voice can be recommended (warning no_voice_available), synthesis, voice_selection and estimate are null: pick a voice with beatra.voices.list and call again with that voice.

To switch to an alternative voice within 2 days, change only voice (and model if needed), keep every other argument, and call again with a new client_request_id. The AI Director reuses its earlier reading, never charges twice for the same reading, and only rewrites the script for the new voice. The balance check and the limit of two in progress still apply to this call, although the reused reading is not charged.

Performance on each model

The AI Director only writes what the target model can perform:

  • ElevenLabs: emotion changes line by line, performances such as whispering and shouting, and sound effects such as laughter and sighs.
  • MiniMax: one emotion for the whole script, plus pauses, pronunciation and sound effects.
  • Doubao: one emotion for the whole script, plus pauses and pronunciation; whether a voice follows emotion, acting notes and previous text depends on the voice.

When the chosen voice cannot perform an effect the user asked for, the AI Director performs the rest and says in a warning what it could not do and what it used instead. For line-by-line performance, choose a voice that performs line by line.

Warnings

The top-level warnings are the AI Director's notes for the user: pass them on. Each script's warnings are the free validation's notes for that script and are usually empty.

codeMeaning
direction_unavailableThe AI Director could not direct all or part of the text. Those sentences come back as written, and that part is not charged. field: "voice" means the voice you named cannot read this script (for example, the text is in a language the voice does not speak); choose another voice.
effect_unavailableThe chosen voice cannot perform an effect the user asked for, or the effect could not be kept because the text is close to the voice's per-call length limit. effect names it and substitute says what was used instead (null when nothing was). effect: "voice_gender" means no voice of the requested gender reads this language yet, so the best other voice was recommended.
instruction_unsupportedThe instructions ask for something not yet supported, such as translation, rewriting the text or assigning several speakers. That part is ignored.
no_voice_availableNo voice can be recommended: none reads this language, the model you named has no voice for this language, the text is longer than every voice's limit, or the voices that read it are temporarily unavailable. The script still comes back.

Billing and task status

The AI Director reasons before it annotates and is charged after completion from actual input, output and reasoning tokens. The price and an estimate per 1,000 characters are in the capability catalog, GET /v1/catalog/capabilities/speech_script. To start, the balance must cover the estimate for the text, counting at least 1,000 characters, plus the estimate of any other AI Director task in progress on the account: at 146 credits per 1,000 characters, a 10,000-character text needs 1,460. Otherwise the call returns 402 insufficient_balance with details.reason: director_estimate and nothing is created. The charge is the actual usage, usually below the estimate. The direction is not charged when directed is false or when a reading from the last 2 days is reused. When directed is true, it is charged from actual tokens, including when the AI Director finds the text needs no added direction and returns it unchanged. Synthesis is charged separately as text to speech; each script's estimate shows that price.

An account can have at most two AI Director tasks in progress at a time, and the platform runs a limited number at once. A third request returns 429 rate_limited with details.reason: director_inflight_limit (over MCP, the plan's tasks-at-a-time limit can answer first); while the AI Director is at capacity, a request returns 429 with details.reason: director_busy. Neither starts a task or charges anything. For director_inflight_limit, wait for one of the account's AI Director tasks to finish, then submit again; for director_busy, retry after details.retry_after_seconds (over REST, the Retry-After header carries the same seconds). If the AI Director is temporarily unavailable, the call returns 503 model_unavailable; there is no model to switch to, so retry later.

queued and running are not failures; keep polling the same task_id. On a lost create response, retry the identical arguments with the same client_request_id; if any input changes, use a new ID. The task list (GET /v1/tasks) shows only a summary of the result: it is marked truncated: true and has no synthesis. To get a script you can synthesize, read the task by ID (beatra.tasks.get or GET /v1/tasks/{task_id}). The AI Director's result is a script, not a creation: it does not appear in your creations, and the console shows it as "AI Director" in activity and billing.

REST API

Direct protocol integrations may use POST /v1/audio/speech/scripts and the shared task endpoints, and send each script's synthesis unchanged as the body of POST /v1/audio/speech. Follow the generated API operation. Skill + MCP is the recommended integration; use REST for custom, non-agent applications.

Last updated

On this page

Markdown