> This page is for Model APIs.

> Sarvam Voice Agents (Samvaad) is a voice AI agent platform for Indian languages: build, test and deploy AI calling agents on your own phone numbers, from ₹3 a minute. Start here: https://docs.sarvam.ai/voice-agents/why-voice-agents.
> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt. For a section index, append `/llms.txt` to any section URL (for example https://docs.sarvam.ai/voice-agents/llms.txt).
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# Support Voice Notes

> Build a WhatsApp support bot that transcribes inbound voice notes, drafts a reply with a Sarvam chat model, and speaks that reply in a cloned support-agent voice.

> **Note**
>
> Instant Voice Cloning is in **beta**. This cookbook uses it for the spoken reply; inbound transcription still uses Saaras.

## **Overview**

This cookbook builds a WhatsApp support bot whose spoken replies sound like a specific agent, not a stock speaker. It clones that agent once from a short reference clip, then on every inbound message it transcribes with **Saaras**, drafts a reply with a **Sarvam chat model**, and speaks the reply with **Instant Voice Cloning**.

The [WhatsApp Bot](/api/cookbook/guides/whats-app-bot) guide is the same channel with a Bulbul speaker. Use this page when the reply has to be a named person's voice.

**What you'll build:**

* A saved cloned voice from a 10-15 second support-agent clip
* A Flask webhook that receives WhatsApp text and voice notes via Twilio
* Automatic transcription of inbound voice notes
* A short support reply from a Sarvam chat model
* A spoken reply in the cloned agent voice, attached as WhatsApp audio

### Business Value

* Keep one familiar agent voice on a channel customers already have open
* Reply in Hindi, Tamil, or any supported language from a single English (or other) reference clip
* Avoid hiring per-language voice talent for routine status and FAQ replies

#### Customer Support

A known agent's voice confirms an order or walks through a refund, instead of a generic TTS speaker.

#### Collections & Reminders

Payment reminders in the same voice the customer already spoke to on a prior call.

#### Field Networks

A team lead's voice answers routine questions from delivery or field staff over WhatsApp.

## **How It Works**

#### Clone the agent once

Send a clean reference clip to `voice_cloning.create_voice`. Keep the returned `voice_id` — every later reply uses it.

#### Receive the message

Twilio POSTs every inbound WhatsApp message, text or voice note, to your Flask webhook.

#### Transcribe and draft

Voice notes go through **Saaras**. Typed or transcribed text goes to a **Sarvam chat model** with a short support persona.

#### Speak the reply

`voice_cloning.convert` synthesizes the reply with the saved `voice_id`. The bot attaches that audio to the WhatsApp response and still sends the text if synthesis fails.

> **Warning**
>
> **Voice cloning requires consent.** Only clone a voice you have the right to use. See [Commercial Licensing](/api/getting-started/commercial-licensing).

## **1. Prerequisites**

* Python 3.9 or higher
* `sarvamai` ≥ `0.1.37`
* A Sarvam API key from [Key Management](https://dashboard.sarvam.ai/key-management)
* A 10-15 second, single-speaker clip of the support agent, saved as `reference.wav`. See [Prepare Reference Audio](/api/api-guides-tutorials/voice-cloning/how-to/prepare-reference-audio).
* A [Twilio](https://console.twilio.com) account with the WhatsApp Sandbox (or a WhatsApp-enabled Sender)
* [ngrok](https://ngrok.com/download) to expose the local server

> **Note**
>
> This cookbook uses the free **Twilio Sandbox for WhatsApp** for testing. Production needs a registered WhatsApp Sender. See [Twilio's WhatsApp docs](https://www.twilio.com/docs/whatsapp). For Sandbox join-code and webhook setup, follow [WhatsApp Bot](/api/cookbook/guides/whats-app-bot#3-configure-twilio-for-whatsapp) — the Twilio side is the same.

## **2. Install the SDK and Dependencies**

```bash
pip install "sarvamai>=0.1.37" flask twilio python-dotenv requests
```

## **3. Set Environment Variables**

```bash
export SARVAM_API_KEY="your-sarvam-api-key"
export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="your-twilio-auth-token"
# Optional: reuse a voice created earlier instead of uploading the clip on every start
# export VOICE_ID="svc-your-voice-id"
```

> **Tip**
>
> Load keys from the environment. If `VOICE_ID` is unset, the script clones `reference.wav` once at startup and prints the new id — save that value so later runs skip create.

## **4. The Full Bot Script**

Copy this into `support_voice_notes.py`. It creates (or reuses) the agent voice, then handles every inbound WhatsApp message.

```python
import base64
import logging
import os
import uuid
from pathlib import Path

import requests
from dotenv import load_dotenv
from flask import Flask, Response, request, send_from_directory
from sarvamai import SarvamAI
from twilio.twiml.messaging_response import MessagingResponse

load_dotenv(override=True)

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__)
sarvam = SarvamAI(api_subscription_key=os.environ["SARVAM_API_KEY"])

AUDIO_DIR = Path("audio_replies")
AUDIO_DIR.mkdir(exist_ok=True)

SYSTEM_PROMPT = (
    "You are a support agent for a consumer app, chatting over WhatsApp. "
    "Reply in the customer's language. Keep replies to 2-3 sentences."
)


def resolve_voice_id() -> str:
    existing = os.getenv("VOICE_ID")
    if existing:
        return existing
    with open("reference.wav", "rb") as f:
        created = sarvam.voice_cloning.create_voice(
            file=f,
            name="support-agent",
            language="en-IN",
        )
    voice_id = created.data.voice_id
    logger.info("Created cloned voice %s — set VOICE_ID to reuse it", voice_id)
    return voice_id


VOICE_ID = resolve_voice_id()


@app.route("/audio/<filename>")
def serve_audio(filename):
    return send_from_directory(AUDIO_DIR, filename, mimetype="audio/wav")


@app.route("/whatsapp", methods=["POST"])
def whatsapp_webhook():
    num_media = int(request.form.get("NumMedia", 0))
    twiml = MessagingResponse()

    try:
        if num_media > 0 and "audio" in request.form.get("MediaContentType0", ""):
            media_url = request.form["MediaUrl0"]
            media_response = requests.get(
                media_url,
                auth=(os.getenv("TWILIO_ACCOUNT_SID"), os.getenv("TWILIO_AUTH_TOKEN")),
                timeout=10,
            )
            media_response.raise_for_status()
            audio_file = Path("/tmp/voice_note.ogg")
            audio_file.write_bytes(media_response.content)
            transcript = sarvam.speech_to_text.transcribe(
                file=open(audio_file, "rb"),
                model="saaras:v4",
                language_code="unknown",
            )
            user_text = transcript.transcript
        else:
            user_text = request.form.get("Body", "")

        completion = sarvam.chat.completions(
            model="sarvam-105b",
            messages=[
                {"role": "system", "content": SYSTEM_PROMPT},
                {"role": "user", "content": user_text},
            ],
        )
        reply_text = completion.choices[0].message.content
    except Exception:
        logger.exception("Failed to process incoming WhatsApp message")
        reply_text = "Sorry, I couldn't process that. Please try again in a moment."

    message = twiml.message(reply_text)
    try:
        spoken = sarvam.voice_cloning.convert(
            voice_id=VOICE_ID,
            text=reply_text,
        )
        filename = f"{uuid.uuid4().hex}.wav"
        (AUDIO_DIR / filename).write_bytes(
            base64.b64decode(spoken.audio_b64 or spoken.audio)
        )
        message.media(f"{request.host_url}audio/{filename}")
    except Exception:
        logger.exception("Cloned-voice synthesis failed, sending text-only reply")

    return Response(str(twiml), mimetype="application/xml")


if __name__ == "__main__":
    app.run(port=7861)
```

### How it works

* **`resolve_voice_id`** clones the agent from `reference.wav` once, unless `VOICE_ID` is already set. Create is not on the per-message path.
* **Inbound voice notes** download from Twilio and go to **Saaras** with `language_code="unknown"`, so a Hindi note and a Tamil note work without a code change.
* **`sarvam-105b`** drafts a short reply in the customer's language.
* **`voice_cloning.convert`** speaks that reply with the saved agent voice. Synthesis has its own `try`/`except`, so a cloning failure still leaves the customer with the text reply.
* `language_code` is omitted on convert so the service detects the language from the reply. Pass the code when pronunciation has to be exact — see [Supported languages](/api/api-guides-tutorials/voice-cloning/supported-languages).

> **Note**
>
> WhatsApp only renders a native voice-note bubble for OGG/Opus. A `.wav` reply still plays; it shows as a regular audio attachment.

> **Warning**
>
> This script trusts every request to `/whatsapp`. Before production, validate `X-Twilio-Signature` with `twilio.request_validator.RequestValidator`.

## **5. Run It**

Put `reference.wav` next to the script, point the Twilio Sandbox webhook at `https://<ngrok-host>/whatsapp`, then:

```bash
python support_voice_notes.py
```

```bash
ngrok http 7861
```

Send a text or a voice note to the Sandbox number. You should get a text reply and an audio attachment in the cloned agent's voice.

## **6. Tips & Best Practices**

* **Reference clip:** 10-15 seconds, one speaker, no music. The clone copies pace and emotion, not only timbre. See [Prepare Reference Audio](/api/api-guides-tutorials/voice-cloning/how-to/prepare-reference-audio).
* **Create once:** Reuse `VOICE_ID`. Re-uploading the clip on every process start repeats transcription and can hit the free-plan voice cap.
* **Reply length:** Keep the system prompt explicit. WhatsApp bodies are capped at 1600 characters, and short replies clone more cleanly.
* **One-shot vs stream:** WhatsApp needs a finished file to attach, so this guide uses `convert`. For a live voice agent, use [HTTP streaming](/api/api-guides-tutorials/voice-cloning/how-to/http-streaming) or [WebSocket](/api/api-guides-tutorials/voice-cloning/how-to/websocket-streaming).
* **Sandbox opt-in:** Recipients must send the Sandbox join code first. See [WhatsApp Bot](/api/cookbook/guides/whats-app-bot#3-configure-twilio-for-whatsapp).

## **7. Error Handling**

Retry `429`, `500`, `502`, `503`, and `504` with exponential backoff. Do not retry `400`, `401`, `402`, `403`, `404`, or `422`. Log `request_id` from failed Sarvam responses.

You may also see:

* **400** on clone — both `voice_id` and `ref_audio` were sent, the clip is too long, or the reply is romanized Indic / Urdu. Keep native script in the chat prompt.
* **402** — the subscription cannot generate this request.
* **403** — invalid API key.
* **Twilio 63015** — the sender has not joined the Sandbox.

For the full table, see [Errors & Troubleshooting](/api/getting-started/errors-troubleshooting) and the [Instant Voice Cloning FAQ](/api/api-guides-tutorials/voice-cloning/faq).

## **8. Additional Resources**

* **API overview**: [Instant Voice Cloning](/api/api-guides-tutorials/voice-cloning/overview)
* **Same channel, stock voice**: [WhatsApp Bot](/api/cookbook/guides/whats-app-bot)
* **How-tos**: [Prepare Reference Audio](/api/api-guides-tutorials/voice-cloning/how-to/prepare-reference-audio) · [Clone Across Languages](/api/api-guides-tutorials/voice-cloning/how-to/clone-across-languages) · [HTTP Streaming](/api/api-guides-tutorials/voice-cloning/how-to/http-streaming)
* **Related APIs**: [Speech-to-Text](/api/api-guides-tutorials/speech-to-text/overview) · [Chat Completion](/api/api-guides-tutorials/chat-completion/overview)
* **Twilio**: [Sandbox for WhatsApp](https://www.twilio.com/docs/whatsapp/sandbox)
* **Community**: [Join the Discord Community](https://discord.com/invite/5rAsykttcs)

## **9. Final Notes**

* Keep API keys in environment variables.
* Only clone a voice you have the right to use.
* Create the agent voice once, then reuse `voice_id` on every reply.

**Keep Building!** 🚀