跳转到内容

Avatar Service REST API

此内容尚不支持你的语言。

Unlike the rest of the platform, avatar/voice operations are exposed over a separate REST service, not the GraphQL API at api.wetel.dev/graphql. This service handles text-to-speech (TTS) synthesis, speech-to-text (STT) transcription, and serving the catalog of available 3D avatar models. Requests go to your assigned avatar-service endpoint (referred to below as https://avatar.wetel.dev — confirm the exact hostname for your environment with the platform team).

This page assumes you’re already familiar with Avatar Config and Sessions from the main GraphQL API. See also <vai-avatar> and Voice Interface for the higher-level client components that call these endpoints for you — most integrations should reach for those rather than calling this REST API directly.

Every endpoint below (except the public model catalog and health check) requires an avatar token — a short-lived, HMAC-signed token that is distinct from your regular JWT or API key. Send it as a standard bearer token:

Authorization: Bearer <AVATAR_TOKEN>

You obtain an avatar token from the main GraphQL API, not from this service directly:

  • the avatarToken query (see Avatar Config), or
  • automatically, as part of sdkStart’s response, when using the embed SDK entry point.

Rate limiting: two independent ceilings per route

Section titled “Rate limiting: two independent ceilings per route”

Added 2026-09-17. Every authenticated route below (/tts, /tts/google, /stt, /avatar-session) is governed by two separate, named throttles that must both allow the request:

  • A per-client-IP ceiling (the number quoted in each route’s own “Rate limit” line below) — this existed before 2026-09-17 and is unchanged.
  • A per-tenant ceiling, new as of 2026-09-17, shared across every caller currently authenticated with that tenant’s avatar tokens (regardless of which IP each caller connects from) — set to 20x the per-IP figure on every route.

A request without a valid, current avatarToken (missing header, malformed token, expired token, bad signature) is governed by the per-IP ceiling only — the per-tenant throttle is skipped entirely for it, not silently pooled into a shared “no tenant” bucket. Exceeding either ceiling returns the same 429-equivalent throttling response; there’s no way from the response alone to tell which of the two was hit.

Synthesizes speech from text or SSML.

Auth required: Avatar token (Bearer).

Rate limit: 30 requests/minute per client IP, and 600 requests/minute per tenant (added 2026-09-17 — see Rate limiting: two independent ceilings per route above).

Request body:

FieldTypeRequiredNotes
textstringOne of text/ssmlPlain text to synthesize
ssmlstringOne of text/ssmlSSML markup; takes precedence over text if both are set
voiceNamestringYes
languageCodestringYese.g. en-US
speakingRatenumberNo0.254.0
audioEncodingstringNoMP3 or LINEAR16

Response: 200 OK

FieldTypeNotes
audioContentstringBase64-encoded audio
timepointsarray[{ markName: string, timeSeconds: number }] — SSML mark timing, for lip-sync
Terminal window
curl -X POST https://avatar.wetel.dev/tts \
-H "Authorization: Bearer <AVATAR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, how can I help you today?",
"voiceName": "en-US-Neural2-F",
"languageCode": "en-US"
}'
{
"audioContent": "//uQxAAAAAAAAAAAAAAAAAAAAAAAAAAA...",
"timepoints": []
}

A request/response-shape-compatible variant of /tts, for third-party avatar rendering libraries (such as TalkingHead) that expect the Google Cloud Text-to-Speech request format instead of this service’s native shape.

Auth required: Avatar token (Bearer).

Rate limit: 30 requests/minute per client IP, and 600 requests/minute per tenant (added 2026-09-17 — see Rate limiting: two independent ceilings per route above).

Request body (Google Cloud TTS shape):

{
"input": { "text": "Hello there", "ssml": null },
"voice": { "languageCode": "en-US", "name": "en-US-Neural2-F" },
"audioConfig": { "speakingRate": 1.0 }
}

Only input.text/input.ssml, voice.name, voice.languageCode, and audioConfig.speakingRate are read — any other fields a third-party library sends alongside these (e.g. enableTimePointing) are accepted and ignored, not rejected.

Response: Same shape as /tts{ audioContent: string, timepoints: [...] }.

Transcribes an audio clip to text.

Auth required: Avatar token (Bearer).

Rate limit: 10 requests/minute per client IP — lower than TTS because STT is billed per 15-second audio block upstream, so this limit is intentionally more conservative — and 200 requests/minute per tenant (added 2026-09-17 — see Rate limiting: two independent ceilings per route above).

Request body:

FieldTypeRequiredNotes
audioBase64stringYesBase64-encoded audio, max ~4MB of raw audio (~5,600,000 base64 characters)
audioEncodingstringYesMP3, LINEAR16, WEBM_OPUS, or OGG_OPUS
languageCodestringYese.g. en-US
sampleRateHertznumberNo800048000. If omitted: 48000 for WEBM_OPUS, 16000 for every other encoding.

OGG_OPUS (OGG container, Opus codec) is the native format WhatsApp and Telegram voice notes are recorded in — send one straight through without transcoding it first. It’s a distinct value from WEBM_OPUS because the two use different container formats despite both carrying Opus audio.

Response: 200 OK

FieldTypeNotes
transcriptstring
confidencenumber01
languageCodestring
Terminal window
curl -X POST https://avatar.wetel.dev/stt \
-H "Authorization: Bearer <AVATAR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"audioBase64": "UklGRi...",
"audioEncoding": "WEBM_OPUS",
"languageCode": "en-US"
}'
{
"transcript": "What time does the library close today?",
"confidence": 0.94,
"languageCode": "en-US"
}

If the underlying speech provider is rate-limited or unavailable, this endpoint returns 503 Service Unavailable rather than a generic 500 — worth handling as a distinct, retryable case in your client.

This endpoint uses synchronous transcription, which has a practical ceiling of roughly 60 seconds of audio per call. A typical voice note or conversational turn is well under that; if you need to transcribe something longer, split it into chunks client-side rather than sending one large file.

Negotiates a HOSTED_API (photorealistic video avatar) rendering session. Only relevant if your agent’s avatarBackend is configured to HOSTED_API — see Avatar & 3D Rendering: Avatar rendering options. Not used for the default 3D (CLIENT_3D) rendering path, which is driven entirely by sdkStart’s response instead.

Auth required: Avatar token (Bearer).

Rate limit: 10 requests/minute per client IP, and 200 requests/minute per tenant (added 2026-09-17 — see Rate limiting: two independent ceilings per route above). Lower than TTS/STT because this negotiates a real, per-minute-billed hosted video session with the underlying vendor, not a per-utterance call.

Request body:

FieldTypeRequiredNotes
anamAvatarIdstringNoMust match the value already configured on the agent’s avatarBackend config — sent only so a mismatch can be rejected loudly.
anamAvatarModelstringNoSame as above.

Your own client code never chooses or overrides the vendor persona/model here — the real value comes from the agent’s server-side avatarBackendConfig, embedded in the avatarToken at sdkStart time. These fields exist only so the server can detect and reject a mismatch, not so the browser can request an arbitrary persona.

Response: 200 OK

FieldTypeNotes
sessionTokenstringA vendor session credential. The browser never sees the vendor’s own API key.
Terminal window
curl -X POST https://avatar.wetel.dev/avatar-session \
-H "Authorization: Bearer <AVATAR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{}'
{
"sessionToken": "eyJhbGciOi..."
}

A negotiation failure (missing vendor credentials, vendor outage) returns a generic 500 with no vendor-specific detail — the underlying error is logged server-side, never surfaced to the browser, since it may reference internal config state.

Returns the catalog of available 3D avatar models.

Auth required: None — this is a public catalog listing.

Response: 200 OK

{
"models": [
{
"id": "avatar-01",
"label": "Aria",
"gender": "female",
"path": "/avatars/models/aria.glb"
}
]
}
FieldTypeNotes
idstring
labelstringDisplay name
genderstring
pathstringRelative path to the .glb model asset
Terminal window
curl https://avatar.wetel.dev/avatars/models

A basic health check endpoint — returns a 200 OK when the service is up. No auth required, no meaningful response body to parse; use it only for uptime/liveness checks, not as a functional API.


For the config that determines which voice/avatar a given agent uses by default, see Avatar Config. For how sessions and avatar tokens fit together, see Sessions. For the client-side components that wrap this API, see <vai-avatar> and Voice Interface.