Vapi
Serve Speechify voices to Vapi custom voice via the open-source tts-shims Vapi provider.
Overview
Vapi custom voice calls a webhook you host and expects raw PCM back. The tts-shims repo ships a vapi provider — a small Go binary that answers Vapi’s POST with Content-Type: application/octet-stream mono 16-bit little-endian PCM at the requested sample rate, translating the request into a Speechify stream call under the hood.
Your SPEECHIFY_API_KEY lives on the shim server; Vapi never sees it.
SpeechifyAI Agents — our own voice-agent platform
SpeechifyAI ships its own voice-agents platform powered by Simba 3.2, which ranks #1 on the Artificial Analysis TTS leaderboard as of July 2026. See SpeechifyAI Agents if you want an end-to-end voice-agent stack instead of using Vapi custom voice.
Why a Vapi-specific shim?
Vapi custom TTS doesn’t send an OpenAI-shaped body. It posts a nested message object:
The response contract is equally specific: raw PCM, mono, signed 16-bit little-endian, sample rate matching message.sampleRate, no WAV header. No OpenAI-shaped shim can serve this — hence the dedicated vapi provider.
Prerequisites
- Speechify API key
- Vapi account with a custom-voice-capable plan
- Public HTTPS URL for the shim (Vapi calls it from its cloud)
- Go toolchain locally (only to build the shim)
Build and run the shim
Start it with your Speechify key in the environment:
The provider mounts one route: POST /synthesize. Defaults: voice geffen_32, model simba-3.2.
Verify without a Vapi account
The custom TTS contract is HTTP — you can prove the whole path locally by sending the exact body Vapi sends:
A working shim returns:
Wrap it as WAV locally to spot-check:
Configure Vapi custom voice
In your Vapi assistant config:
Vapi sends the server.secret value as X-VAPI-SECRET; the shim validates that header before calling Speechify. Vapi also supports custom headers and credentialId flows if that’s your credential-management style.
Sample rate matters
Vapi’s documented custom TTS sample rates are 8000, 16000, 22050, and 24000. The response must match message.sampleRate exactly. The shim validates the incoming rate and maps it to Speechify’s stream output format.
The reference test above uses 24000. If you deploy for telephony (8000 / 16000), test with the actual rate before sending live calls through it — sample-rate mismatches surface as choppy or noisy audio, not clean errors.
Never return WAV
Vapi’s contract is raw PCM with Content-Type: application/octet-stream. Do not return a WAV file, MP3, or any container-wrapped audio. The demo saves a local .wav only after the response is written to disk, for humans and tools to spot-check — that’s not what Vapi receives.
Production deployment
- Put the shim behind HTTPS and a service host that holds environment variables (Cloud Run, Fly, Render, ECS — anywhere).
- Set
SPEECHIFY_API_KEY,SHIM_ADDR,SHIM_DEFAULT_MODEL, andVAPI_SECRET. - Rotate
VAPI_SECRETand your Speechify key on a normal schedule — the shim doesn’t cache either. - Add health check monitoring on
GET /healthz.
Resources
Runnable end-to-end demo with run.sh that clones the shim, builds it, and executes the smoke test.
Open-source Go proxy — Vapi, OpenAI, and other provider dialects under cmd/.
Vapi’s request/response contract for custom-voice.
Blog: contract details, sample rate discussion, FAQ.