> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Persona Voice Cloning → Branded Audio

## Scenario

Clone a spokesperson's voice using ElevenLabs' voice cloning API, then generate audio versions of Mavera-generated content using that cloned voice — building a consistent audio identity across all content.

**Flow:** ElevenLabs `POST /voices/add` → get voice\_id → Mavera `POST /generations` → ElevenLabs `POST /text-to-speech/{voice_id}` → branded audio

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time
  EL_KEY = os.environ["ELEVENLABS_API_KEY"]
  EL_BASE = "https://api.elevenlabs.io/v1"
  MV = os.environ["MAVERA_API_KEY"]
  MV_BASE = "https://app.mavera.io/api/v1"
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  os.makedirs("branded_audio", exist_ok=True)
  SAMPLE_FILES = ["voice_samples/intro.mp3", "voice_samples/pitch.mp3", "voice_samples/casual.mp3"]

  # 1. Clone voice with multipart upload
  files = [("files", (os.path.basename(f), open(f, "rb"), "audio/mpeg")) for f in SAMPLE_FILES]
  clone = requests.post(f"{EL_BASE}/voices/add", headers={"xi-api-key": EL_KEY},
      data={"name": "Brand Spokesperson v1",
            "description": "Official brand voice from spokesperson recordings",
            "labels": '{"use_case": "marketing", "language": "en"}'},
      files=files).json()
  for _, (_, fh, _) in zip(SAMPLE_FILES, files): fh.close()
  voice_id = clone["voice_id"]
  print(f"Voice cloned: {voice_id}")
  time.sleep(2)

  # 2. Generate content + convert to branded audio
  SPECS = [
      {"prompt": "Write a 100-word product announcement for an AI analytics dashboard.", "label": "product-launch"},
      {"prompt": "Write a 60-second radio ad script for a B2B marketing platform.", "label": "radio-spot"},
      {"prompt": "Write a 30-second podcast pre-roll ad for a data intelligence tool.", "label": "podcast-preroll"},
  ]
  for spec in SPECS:
      gen = requests.post(f"{MV_BASE}/generations", headers=MV_H,
          json={"prompt": spec["prompt"]}).json()
      text = gen.get("output") or gen.get("content") or ""
      print(f"[{spec['label']}] {len(text.split())} words")
      time.sleep(1)

      tts = requests.post(f"{EL_BASE}/text-to-speech/{voice_id}",
          headers={"xi-api-key": EL_KEY, "Content-Type": "application/json"},
          json={"text": text, "model_id": "eleven_multilingual_v2",
                "voice_settings": {"stability": 0.5, "similarity_boost": 0.8,
                                   "style": 0.3, "use_speaker_boost": True}})
      if tts.status_code == 200:
          path = f"branded_audio/{spec['label']}-cloned.mp3"
          with open(path, "wb") as f: f.write(tts.content)
          print(f"  → {path} ({len(tts.content) // 1024} KB)")
      time.sleep(2)
  ```

  ```javascript JavaScript theme={"dark"}
  import fs from "fs";
  import path from "path";

  const EL_KEY = process.env.ELEVENLABS_API_KEY;
  const EL_BASE = "https://api.elevenlabs.io/v1";
  const MV = process.env.MAVERA_API_KEY;
  const MV_BASE = "https://app.mavera.io/api/v1";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  fs.mkdirSync("branded_audio", { recursive: true });

  const sampleFiles = ["voice_samples/intro.mp3", "voice_samples/pitch.mp3", "voice_samples/casual.mp3"];

  // 1. Clone voice
  const form = new FormData();
  form.append("name", "Brand Spokesperson v1");
  form.append("description", "Official brand voice from spokesperson recordings");
  form.append("labels", JSON.stringify({ use_case: "marketing", language: "en" }));
  for (const fp of sampleFiles)
    form.append("files", new Blob([fs.readFileSync(fp)]), path.basename(fp));

  const clone = await fetch(`${EL_BASE}/voices/add`, {
    method: "POST", headers: { "xi-api-key": EL_KEY }, body: form,
  }).then(r => r.json());
  const voiceId = clone.voice_id;
  console.log(`Voice cloned: ${voiceId}`);
  await new Promise(r => setTimeout(r, 2000));

  // 2. Generate content + audio
  const specs = [
    { prompt: "Write a 100-word product announcement for an AI analytics dashboard.", label: "product-launch" },
    { prompt: "Write a 60-second radio ad for a B2B marketing platform.", label: "radio-spot" },
    { prompt: "Write a 30-second podcast pre-roll ad.", label: "podcast-preroll" },
  ];
  for (const spec of specs) {
    const gen = await fetch(`${MV_BASE}/generations`, { method: "POST", headers: MV_H,
      body: JSON.stringify({ prompt: spec.prompt }) }).then(r => r.json());
    const text = gen.output || gen.content || "";
    console.log(`[${spec.label}] ${text.split(/\s+/).length} words`);
    await new Promise(r => setTimeout(r, 1000));

    const tts = await fetch(`${EL_BASE}/text-to-speech/${voiceId}`, { method: "POST",
      headers: { "xi-api-key": EL_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ text, model_id: "eleven_multilingual_v2",
        voice_settings: { stability: 0.5, similarity_boost: 0.8, style: 0.3, use_speaker_boost: true } }),
    });
    if (tts.ok) {
      const buf = Buffer.from(await tts.arrayBuffer());
      const out = `branded_audio/${spec.label}-cloned.mp3`;
      fs.writeFileSync(out, buf);
      console.log(`  → ${out} (${Math.round(buf.length / 1024)} KB)`);
    }
    await new Promise(r => setTimeout(r, 2000));
  }
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
Voice cloned: vId_a8f3c2d1e9b7
[product-launch] 104 words → product-launch-cloned.mp3 (142 KB)
[radio-spot] 87 words → radio-spot-cloned.mp3 (198 KB)
[podcast-preroll] 62 words → podcast-preroll-cloned.mp3 (89 KB)
```

## Error Handling

<AccordionGroup>
  <Accordion title="Voice cloning sample requirements">Instant cloning accepts 1–25 samples. Provide at least 3 clean samples totaling 1–3 minutes with only the target speaker — no background music or multiple speakers.</Accordion>
  <Accordion title="Character credit limits">Each plan has a monthly character quota (\~500 chars per 100 words). A 401 with `quota_exceeded` means your allocation is exhausted — upgrade or wait for monthly reset.</Accordion>
  <Accordion title="Voice not found after cloning">If TTS returns 404 after cloning, the voice may still be processing. Wait 5–10 seconds and retry. Verify with `GET /voices`.</Accordion>
</AccordionGroup>
