Skip to main content
All posts

Blog

Give your Vapi voice agent a cheaper voice: Kokoro TTS via custom-voice

How to plug EcoHash's Kokoro TTS into a Vapi voice agent as a custom voice: a small adapter server, the assistant config, a local test, and what it costs per minute compared to ElevenLabs.

EcoHash Team6 min read

Vapi lets you swap the voice of an agent for any TTS you want through its custom-voice provider: it POSTs the text to your server and plays back whatever PCM you return. That makes it a clean slot for Kokoro on EcoHash, an open 82M-parameter TTS that costs about $0.006 per minute of generated speech and returns first audio in roughly 120 ms. The whole integration is one small adapter server, about 60 lines: it receives Vapi's voice-request, calls EcoHash's OpenAI-compatible /v1/audio/speech, resamples to the rate Vapi asks for, and sends the raw PCM back. This post walks through the adapter, the assistant config, a local test that does not need a phone call, and the per-minute math against ElevenLabs.

Swap the voice, keep the agent. A 60-line adapter puts Kokoro behind Vapi at about $0.006 per minute of speech, roughly 7x below ElevenLabs Flash list price.

Who this is for

  • Teams running voice agents on Vapi who want to cut the TTS line item.

  • Developers who want an open TTS model behind their agent instead of a proprietary voice.

  • Anyone comparing per-minute costs across TTS providers before scaling outbound calls.

What you can build

  • A Vapi assistant that speaks with Kokoro at a fraction of the usual TTS cost.

  • The same adapter pattern for any OpenAI-compatible TTS endpoint.

  • A post-call pipeline that also transcribes recordings with whisper-large-v3-turbo on the same API key.

Facts

  • TTS model: kokoro-82m, open weights, served on EcoHash

  • Price: $1 per 1M audio tokens, about $0.006 per minute of speech

  • First audio: ~120 ms measured end-to-end on EcoHash

  • Vapi slot: custom-voice provider, HTTP POST, raw PCM response

  • Sample rates: Vapi requests 8000, 16000, 22050, or 24000 Hz; Kokoro is 24 kHz native

  • Adapter: integrations/vapi in ecohash-examples

  • API base URL: https://api.ecohash.com/v1

How the pieces fit

Vapi does not call EcoHash directly, because its custom-voice contract is not the OpenAI speech API. When the agent needs to speak, Vapi POSTs a JSON voice-request with the text and a target sample rate, and expects headerless 16-bit mono PCM back at exactly that rate. So you run a small adapter in the middle:

  1. Vapi POSTs {"message": {"type": "voice-request", "text": ..., "sampleRate": ...}} to your server.

  2. The adapter calls EcoHash /v1/audio/speech with model: kokoro-82m and response_format: pcm.

  3. Kokoro returns 24 kHz PCM; the adapter resamples if Vapi asked for a different rate.

  4. The adapter answers with application/octet-stream PCM, and the caller hears Kokoro.

Vapi custom-voice flow: Vapi assistant to adapter to EcoHash Kokoro and back

The adapter

The full server is in integrations/vapi/server.py. The core of it:

@app.post("/synthesize")
async def synthesize(request: Request) -> Response:
    if VAPI_SECRET and request.headers.get("x-vapi-secret") != VAPI_SECRET:
        raise HTTPException(status_code=401)
msg = (await request.json()).get("message", {})
text = msg.get("text", "")
sample_rate = int(msg.get("sampleRate", 24000))

async with httpx.AsyncClient(timeout=30) as client:
    r = await client.post(
        "https://api.ecohash.com/v1/audio/speech",
        headers={"Authorization": f"Bearer {ECOHASH_KEY}"},
        json={"model": "kokoro-82m", "voice": "af_heart",
              "input": text, "response_format": "pcm"},
    )

pcm = resample(r.content, 24000, sample_rate)
return Response(content=pcm, media_type="application/octet-stream")</code></pre><p>Run it and expose it publicly (ngrok is fine for a first test):</p><pre><code>pip install fastapi uvicorn httpx numpy

export ECOHASH_API_KEY=eco_... # create one at console.ecohash.com export VAPI_SECRET=your-webhook-secret uvicorn server:app --host 0.0.0.0 --port 8000

The key comes from the EcoHash console: open API Keys, create one, and copy the eco_ value into ECOHASH_API_KEY.

EcoHash console API Keys page with the Create Key button

Point the assistant at it

In your assistant config, set the voice to the custom-voice provider with your adapter's URL:

{
"voice": {
"provider": "custom-voice",
"server": {
"url": "https://your-host.example.com/synthesize",
"secret": "your-webhook-secret",
"timeoutSeconds": 30
}
}
}

If you prefer the dashboard, it is the same three fields under Assistant → Voice: set the provider to Custom voice and paste your adapter's /synthesize URL.

Vapi assistant Voice settings: Custom voice provider with the adapter Server URL

Vapi sends the secret back in the x-vapi-secret header on every request, and the adapter rejects anything that does not match.

Test it without making a call

You can verify the whole path from your terminal by sending the same request Vapi would:

curl -s http://localhost:8000/synthesize 
-H "Content-Type: application/json"
-H "x-vapi-secret: your-webhook-secret"
-d '{"message":{"type":"voice-request","text":"Your order number seven two nine is confirmed.","sampleRate":16000}}'
-o reply.pcm

ffplay -f s16le -ar 16000 -ch_layout mono reply.pcm

We ran this exact loop while writing the post, at 8, 16, and 24 kHz, and fed the resampled PCM back into whisper-large-v3-turbo on EcoHash. The transcription came back word for word, so the resampling holds up at telephony rates.

What it costs

Kokoro on EcoHash is billed at $1 per 1M audio tokens, and a second of generated speech is 100 tokens. One minute of speech is 6,000 tokens, or $0.006. List prices for the common alternatives, checked 2026-07-13:

  • Kokoro on EcoHash · List price: $1 / 1M audio tokens · Rough cost per audio minute: ~$0.006

  • ElevenLabs Flash/Turbo (API) · List price: $0.05 / 1k characters · Rough cost per audio minute: ~$0.04

  • ElevenLabs Multilingual (API) · List price: $0.10 / 1k characters · Rough cost per audio minute: ~$0.08

The per-minute figures for ElevenLabs assume roughly 800 spoken characters per minute; your text and pacing will move them. Even so, the gap is around 7x to 13x, which adds up quickly on high-volume outbound calls. Voice quality is a judgment call: Kokoro is a small open model, so test it on your own scripts before switching production traffic.

Bonus: run the brain on EcoHash too

Vapi also supports a custom OpenAI-compatible LLM. In the dashboard, open your assistant's Model section, choose Custom LLM, and set the endpoint to https://api.ecohash.com/v1 with your EcoHash key as the API key and a model such as llama-3.1-8b-instruct. That puts the LLM and the voice on one provider and one bill; keep replies short in the system prompt so turns stay snappy.

Limitations

  • Live call transcription stays on Vapi's built-in transcribers. Vapi's custom transcriber slot expects a streaming WebSocket, and EcoHash's transcription endpoint is batch. Use EcoHash whisper-large-v3-turbo for post-call transcription instead.

  • Latency depends on where you host the adapter. Kokoro's first audio is ~120 ms measured end-to-end on EcoHash; keep the adapter close to your region and the total stays comfortable for calls, but measure your own setup before promising numbers.

  • The cost table uses list prices on their published pages as of the date above. Recheck before you build a business case on them.

  • Model IDs and voices change. Confirm kokoro-82m and the voice list on /models/kokoro-82m before you build.

FAQ

Can Vapi use any TTS provider? Yes, through the custom-voice provider. Vapi POSTs the text to your server and plays whatever PCM you return, so anything with a synthesis API works behind a small adapter.

Why do I need an adapter at all? Vapi's custom-voice contract is its own JSON request and raw PCM response, not the OpenAI speech API. The adapter translates between the two and resamples the audio.

What sample rate should I return? Exactly what Vapi asks for in sampleRate. The adapter in this post resamples Kokoro's 24 kHz output to 8000, 16000, or 22050 Hz when needed.

How much does Kokoro cost on EcoHash? $1 per 1M audio tokens, which is about $0.006 per minute of generated speech. See /pricing.

Can I also do speech-to-text on EcoHash for Vapi calls? Not live: Vapi's custom transcriber needs streaming input. For recordings and post-call analysis, whisper-large-v3-turbo on the same key works well.

Which voices does Kokoro have? Several; this post uses af_heart. See /models/kokoro-82m.

Next steps