Sending Voice Messages to an Agent
此内容尚不支持你的语言。
If your integration already receives voice notes — a WhatsApp/Telegram bridge, a call-center recording, a voice-memo upload in your own app — you can transcribe one and feed it into a Wetel agent using the same avatarToken the <vai-avatar> embed widget uses internally. Nothing restricts that token to the widget; this page documents calling it directly from your own backend.
This is a practical, working walkthrough. For the concepts behind each call, see Core API Flow and Authentication.
Prerequisite
Section titled “Prerequisite”You need a working sdkStart call already. If you don’t have one yet, set that up first — see Sessions API: sdkStart. Everything below assumes you can already call sdkStart server-side with your API key and get back a sessionId and avatarToken.
Step 1: start a session, keep the avatarToken
Section titled “Step 1: start a session, keep the avatarToken”const startRes = await fetch(WETEL_GRAPHQL_URL, { method: "POST", headers: { "Content-Type": "application/json", "x-huat-platform": "customer", "x-api-key": WETEL_API_KEY, // server-side only, never send this to a client }, body: JSON.stringify({ query: `mutation Start($input: SdkStartInput!) { sdkStart(input: $input) { sessionId avatarToken } }`, variables: { input: { agentId: YOUR_AGENT_ID } }, }),});const { data } = await startRes.json();const { sessionId, avatarToken } = data.sdkStart;avatarToken is the credential every call below authenticates with — it’s session-scoped (bound to this sessionId, since 2026-09-08), so keep the pair together.
Step 2: send the audio to /stt
Section titled “Step 2: send the audio to /stt”/stt lives on the avatar-service host, not the main GraphQL API — a separate REST service with its own hostname. Base64-encode your audio and POST it with the avatarToken as a bearer token.
curl -X POST https://avatar.wetel.dev/stt \ -H "Authorization: Bearer <AVATAR_TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "audioBase64": "T2dnUwACAAAAAAAAAAB...", "audioEncoding": "OGG_OPUS", "languageCode": "en-US" }'Or from the same backend that just called sdkStart:
const audioBase64 = rawAudioBuffer.toString("base64"); // your voice-note bytes
const sttRes = await fetch("https://avatar.wetel.dev/stt", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${avatarToken}`, }, body: JSON.stringify({ audioBase64, audioEncoding: "OGG_OPUS", // native WhatsApp/Telegram voice-note format languageCode: "en-US", }),});const { transcript, confidence, languageCode } = await sttRes.json();OGG_OPUS is exactly what WhatsApp and Telegram voice notes already are — an OGG container with Opus-encoded audio. You do not need to transcode the file before sending it; forward the raw bytes as-is.
Step 3: read the response
Section titled “Step 3: read the response”{ "transcript": "What time does the library close today?", "confidence": 0.94, "languageCode": "en-US"}| Field | Type | Notes |
|---|---|---|
transcript | string | The transcribed text |
confidence | number | 0–1 |
languageCode | string | Echoes back the language actually recognized |
There’s no audio or intermediate state to clean up — this call is stateless; the transcript is everything you need going forward.
Step 4: feed the transcript into the agent
Section titled “Step 4: feed the transcript into the agent”Send the transcript as a normal turn via sdkSendMessage — same mutation, same credential, as if the user had typed it.
await fetch(WETEL_GRAPHQL_URL, { method: "POST", headers: { "Content-Type": "application/json", "x-huat-platform": "customer", Authorization: `Bearer ${avatarToken}`, // avatarToken here, not x-api-key }, body: JSON.stringify({ query: `mutation Send($input: SdkSendMessageInput!) { sdkSendMessage(input: $input) }`, variables: { input: { sessionId, text: transcript } }, }),});sdkSendMessage returns immediately — the agent’s reply arrives asynchronously over the sessionEvents subscription, exactly like any other turn. See Events & Subscriptions if you haven’t wired that up yet.
Constraints to know before you build on this
Section titled “Constraints to know before you build on this”- Accepted
audioEncodingvalues:MP3,LINEAR16,WEBM_OPUS,OGG_OPUS. Don’t send anything else — it’s rejected as a validation error before it reaches the speech provider. - ~60 seconds per call.
/sttuses synchronous transcription, which has a practical ceiling around a minute of audio. A single voice note or conversational turn is comfortably under that; if you’re transcribing something longer (a recorded call, a long memo), split it client-side into chunks and call/sttonce per chunk. - 10 requests/minute per avatar token, enforced consistently across the fleet. STT is billed per 15-second audio block upstream, which is why this limit is tighter than
/tts’s 30/min — see Avatar Service REST API:POST /sttfor the full reference. - Max request size: roughly 4MB of raw audio (~5,600,000 base64 characters). Comfortably more than any single voice note; if you’re hitting this, you’re likely sending something other than a short recorded message.
- Sample rate: optional — if you omit
sampleRateHertz,OGG_OPUS(along with everything exceptWEBM_OPUS) defaults to 16kHz, which matches WhatsApp’s native voice-note rate. You don’t need to set this explicitly for a typical voice note.
See also
Section titled “See also”- Avatar Service REST API — the full
/stt//ttsreference this page builds on. - Sessions API —
sdkStart,sdkSendMessage, and the rest of the SDK flow. - Voice Interface — how
<vai-avatar>uses this same/sttendpoint internally for a live, in-browser voice conversation. - Headless Agents (No UI Required) — if you’re bridging a messaging channel end to end, not just transcribing one message.
- Image Understanding for an Agent — the equivalent walkthrough for image attachments feeding a vision-capable model call.