---
title: "Vapi custom-voice webhook (raw PCM for voice agents)"
method: POST
path: "/api/v1/vapi/speech"
tags: ["Public API v1"]
---

# Vapi custom-voice webhook (raw PCM for voice agents)

`POST /api/v1/vapi/speech`

Point a Vapi assistant's `custom-voice` provider at this URL to give it a Vietnamese voice. Vapi POSTs `{message: {type: "voice-request", text, sampleRate}}` and expects raw mono 16-bit little-endian PCM at exactly that rate — which is what this returns, streamed as it is generated. Authenticate by putting your VieNeu API key in the assistant's `server.secret`; Vapi sends it as `X-VAPI-SECRET`. Choose the voice with `?voiceId=` on the URL (and `?engine=` for v4), since Vapi's payload has no field for it. AI refinement is never applied here: an agent needs the reply to start, not an extra model round-trip. Billed per submitted character like the rest of /v1, and refunded if the audio is cut short.

## Query parameters

- `voiceId` string
- `engine` 'v3' | 'v4'

## Request body

- VapiVoiceRequestDto
  - `message` VapiVoiceMessageDto, required
    - `type` string, required
    - `text` string, required
    - `sampleRate` 8000 | 16000 | 22050 | 24000, required

## Response `200`

Raw mono 16-bit little-endian PCM at exactly the `sampleRate` Vapi asked for, streamed as it is generated. Headerless — the rate is the one you sent.

## Other responses

- `400` — Payload is not a `voice-request` message, `text` is empty, or `sampleRate` is not one Vapi and the worker share.
- `401` — API key missing, malformed, or revoked.
- `403` — The grant is out of tokens or expired, or the plan does not allow the requested engine. A daily/weekly cap comes back as 429 instead.
- `429` — Rate limit or token quota exceeded. When the limit came from the application the response carries `Retry-After` (seconds) and the `X-RateLimit-*` headers and is counted against your API key; a 429 with none of those headers came from the edge proxy and is counted against your source address, shared with every other key calling from it. Back off on either.
- `503` — No worker answered within the deadline. Vapi’s `fallbackPlan` should handle this.

---

[API](https://skmtc.dev/vieneu/apis/vieneu-api.md) · [All operations](https://skmtc.dev/vieneu/apis/vieneu-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/vieneu/vieneu-api/revisions/11c8166a6c4d/schema)
