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

# Vapi

> Use Lokutor speech-to-text and text-to-speech in a Vapi assistant through an open adapter service, as a custom transcriber and a custom voice.

[Vapi](https://vapi.ai) lets you bring your own speech providers: a **custom transcriber** (a WebSocket that receives the call audio and returns transcripts) and a **custom voice** (an HTTP endpoint that returns raw audio for a sentence). Lokutor is not a built-in Vapi provider. The open-source [vapi-lokutor-adapter](https://github.com/lokutor-ai/vapi-lokutor-adapter) implements both of Vapi's protocols and forwards to the Lokutor API, so you can use Lokutor in a Vapi assistant today by running that small service yourself.

> [!NOTE]
>
> The adapter was tested against Vapi's published protocol documentation with a local mock, and against the live Lokutor API. It has not yet been run on a Vapi account. Treat the first call as a test, and tell us if Vapi behaves differently from its documentation.

## How it fits together

```
Vapi ── wss  /api/custom-transcriber ──▶ adapter ── wss  /ws/stt ───────────▶ api.lokutor.com
Vapi ── POST /api/synthesize ──────────▶ adapter ── POST /v1/tts/synthesize ▶ api.lokutor.com
```

| Vapi | What the adapter does | Lokutor endpoint |
| :- | :- | :- |
| Custom transcriber | Receives Vapi's 16 kHz 16-bit stereo PCM, keeps the customer channel as mono, returns `transcriber-response` messages (`partial` while the caller speaks, `final` at the end of the utterance) | [`WS /ws/stt`](https://docs.lokutor.com/sdk/stt#raw-ws-stt-protocol) |
| Custom voice | Receives Vapi's `voice-request`, synthesizes the text, resamples the 44.1 kHz result to the `sampleRate` Vapi asked for (8000, 16000, 22050, 24000 or 44100 Hz), returns raw mono 16-bit little-endian PCM | [`POST /tts/synthesize`](https://docs.lokutor.com/api-reference/rest#post-/tts/synthesize-—-tts-synthesis) |

Recognition covers English, Spanish, Catalan, Galician, Basque, Portuguese, French, Italian and German ([languages](https://docs.lokutor.com/voices-languages-models.md)). Voices are the slots `F1`-`F5` and `M1`-`M5`.

Usage is billed to your Lokutor account at the [rate card](https://docs.lokutor.com/api-reference/rate-card.md) prices: synthesis per character and recognition per second of audio sent. Every transcriber connection also counts as one of your account's [concurrent sessions](https://docs.lokutor.com/pricing.md#limits).

## 1. Run the adapter

You need a Lokutor [API key](https://docs.lokutor.com/authentication.md) and a public HTTPS address for the adapter (Vapi cannot reach `localhost`; use your own reverse proxy or a tunnel for testing).

```bash theme={null}
git clone https://github.com/lokutor-ai/vapi-lokutor-adapter
cd vapi-lokutor-adapter
docker build -t vapi-lokutor-adapter .
docker run -p 3000:3000 \
  -e LOKUTOR_API_KEY="$LOKUTOR_API_KEY" \
  -e ADAPTER_SECRET="a-long-random-string" \
  vapi-lokutor-adapter
```

Always set `ADAPTER_SECRET`: without it, anyone who finds the URL can spend your Lokutor balance. The adapter accepts it as an `x-vapi-secret` header or as `Authorization: Bearer <secret>`. The other settings (default voice and language, upstream URL, timeout) are in the repository README.

## 2. Create the credential in Vapi

In the Vapi dashboard, add a **Custom Credential** of type **Bearer Token** whose token is your `ADAPTER_SECRET`, and note its id.

## 3. Configure the assistant

Vapi does not send a voice id to a custom voice, so put the voice and language in the server URL's query string. For the transcriber, `language` fixes the recognition language; leave it out and Lokutor detects it.

```json theme={null}
{
  "name": "Lokutor assistant",
  "transcriber": {
    "provider": "custom-transcriber",
    "server": {
      "url": "wss://adapter.example.com/api/custom-transcriber?language=en",
      "credentialId": "YOUR_VAPI_CUSTOM_CREDENTIAL_ID"
    }
  },
  "voice": {
    "provider": "custom-voice",
    "server": {
      "url": "https://adapter.example.com/api/synthesize?voice=F1&language=en",
      "credentialId": "YOUR_VAPI_CUSTOM_CREDENTIAL_ID",
      "timeoutSeconds": 30
    }
  },
  "model": {
    "provider": "openai",
    "model": "gpt-4o-mini",
    "messages": [{ "role": "system", "content": "You are a helpful assistant." }]
  }
}
```

Replace `adapter.example.com` with your adapter's address. Use any language model Vapi supports; the model section is Vapi's, not Lokutor's.

## Behaviour to know about

* **The voice is not streamed.** The adapter waits for the whole sentence from `POST /tts/synthesize` and then answers, so time to first audio is the synthesis time of the full sentence plus the network hop to the adapter, not the streaming first-audio latency of `WS /ws/tts`.
* **Only the customer channel is transcribed.** Vapi's second (assistant) channel is ignored.
* **End of speech is Lokutor's.** An utterance ends after about 0.7 s without speech ([Speech-to-Text](https://docs.lokutor.com/sdk/stt.md#live-transcription)); Vapi's own endpointing settings do not apply to a custom transcriber.
* **Errors.** If Lokutor refuses a synthesis (unknown voice, balance, capacity) the adapter answers `502`, or `503` with `Retry-After` when synthesis is at capacity, so a Vapi `fallbackPlan` voice can take over. See [Error Handling](https://docs.lokutor.com/guides/error-handling.md).
* [Expression tags](https://docs.lokutor.com/api-reference/expression-tags.md) such as `[laugh]` in the text Vapi sends are passed to Lokutor unchanged.

> [!TIP]
>
> If you want a full voice agent rather than only speech providers, the [hosted agent API](https://docs.lokutor.com/api-reference/voice-chat.md) runs recognition, the language model, tools and synthesis in one connection.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.