Skip to content

Agents

An Agent is a persona — a persona prompt, voice/avatar configuration, and (optionally) an attached conversation Workflow — that a caller starts Sessions against. This page is the exhaustive field-by-field reference for every Agent operation. For a conceptual walkthrough of what an Agent is and how it fits into the platform, see Agents and Core API Flow.

All operations on this page require a valid JWT (Authorization: Bearer <token>) issued to a user belonging to the tenant that owns the Agent — see Authentication. Every Agent is scoped to the caller’s tenant: you can never read, update, or delete another tenant’s Agent, even by guessing its id.

Every request in this reference must include the x-huat-platform: customer header in addition to standard GraphQL headers.

All Agent queries and mutations return this shape (mutations return the full, freshly-updated object):

FieldTypeNotes
idInt!
nameString!
personaPromptString!The system prompt defining the agent’s persona/behavior.
positionString!Free-text role/title label (e.g. “Support Assistant”).
subjectStringFree-text subject/domain label.
openingGreetingStringSpoken/typed opening line when a session starts, if set. Ignored for any agent with a workflowId attached — see the callout below.
useCaseAgentUseCase!Enum — see below.
statusAgentStatus!ACTIVE | INACTIVE.
workflowIdIntAttached Workflow id, if any. See the workflow-publish gotcha in Workflows — an agent’s workflow must be published, not just saved, to actually run.
voiceTierVoiceTier!STANDARD | WAVENET | NEURAL2 | CHIRP3_HD | STUDIO.
ttsVoiceStringTTS voice name — see agentConfigOptions below for valid values.
sttLanguageCodeStringSpeech-to-text primary language (BCP-47). null = use ttsLang. See Speech-to-text languages. live in production since 2026-10-01.
sttAlternativeLanguageCodes[String!]Speech-to-text fallback languages. null = platform default (English + Malay), [] = primary only. live in production since 2026-10-01.
ttsLangStringTTS language code.
speakingRateFloat
vadThresholdFloat!Voice-activity-detection sensitivity.
avatarGlbStringAvatar 3D model filename — see agentConfigOptions.
avatarGenderAvatarGenderCodeF | M.
lipsyncLangString
evaluationPromptTemplateStringCustom prompt used by the async post-session Evaluation.
evaluationRecommendationLabels[String!]Custom label set for Evaluation recommendations.
externalSyncUrlString
externalSyncSecretRefString
externalSyncOwnerIdInt
externalSyncSchemaConfigJSON
llmProviderOverrideLlmProviderTypePilot — which model provider answers this agent’s turns. See Agents: LLM provider override. null means “use the platform default.”
llmModelOverrideStringPilot — which specific model, only meaningful when llmProviderOverride is BEDROCK_MANTLE. See the same section.
createdAtDateTime!
updatedAtDateTime!

openingGreeting does nothing once you attach a workflow

Section titled “openingGreeting does nothing once you attach a workflow”

openingGreeting only fires for agents with no workflowId set. The moment an agent has a published workflow attached, the workflow’s own graph is entirely responsible for producing the first reply — including the greeting for a bare “Hi.” — and openingGreeting is silently never read. This is intentional, not a bug to design around: a separate, statically-configured greeting racing against the workflow’s own first response used to produce a double-greeting on session start, which is why the static greeting is skipped entirely for workflow-driven agents rather than trying to sequence the two. If you need a specific greeting behavior for a workflow-driven agent, build it into the workflow graph itself (e.g. a dedicated branch or node for the first turn), not openingGreeting.

enum AgentUseCase {
CUSTOM
EDUCATION
INTERVIEW
ONBOARDING
SALES
SUPPORT
}
enum AgentStatus {
ACTIVE
INACTIVE
}

voiceTier, useCase, status, and avatarGender are real GraphQL enums — introspect them directly if you need the canonical list ({ __type(name: "AvatarGenderCode") { enumValues { name } } }) rather than hardcoding values from this page.


Live in production since 2026-10-01.

Each agent can carry its own speech-to-text (STT) language settings. They are read by every Wetel-owned voice client (the Telegram channel connector and <vai-avatar>), so one agent hears the same languages on every channel.

FieldMeaning
sttLanguageCodeThe primary language the agent expects to hear, as a BCP-47 code (e.g. en-US, ms-MY, cmn-Hans-CN). null (the default) means “use ttsLang”. It can differ from ttsLang: an agent can reply in English but mostly hear Mandarin.
sttAlternativeLanguageCodesThe fallback languages the speech provider may switch to when the audio is not in the primary. null (the default) = the platform default, English (en-US) and Malay (ms-MY), minus the primary. [] = recognise in the primary only. A list = exactly those languages.

Rules for sttAlternativeLanguageCodes (a violation is rejected with a 400; nothing is silently truncated):

  • At most 3 entries. Past 3 the speech provider ignores the entire list, so the API rejects it instead.
  • No duplicates (compared case-insensitively).
  • It must not contain the effective primary (sttLanguageCode, else ttsLang). List only the languages besides the primary.
  • Each entry, and sttLanguageCode, must look like a BCP-47 code (en-US, cmn-Hans-CN, zh). Underscores (en_US) and whitespace are rejected. The pattern is a shape check only: a well-formed code the speech provider does not support fails at recognition time, not at save time.

createAgent and updateAgent. Both fields are optional on both mutations. On updateAgent, omitting a field leaves it unchanged, and sending null resets it to the default.

mutation SetAgentSttLanguages($id: ID!, $input: UpdateAgentInput!) {
updateAgent(id: $id, input: $input) {
id
sttLanguageCode
sttAlternativeLanguageCodes
}
}
{
"id": "42",
"input": {
"sttLanguageCode": "en-US",
"sttAlternativeLanguageCodes": ["ms-MY", "cmn-Hans-CN"]
}
}

Where the settings are used

  • Telegram voice notes (channel connector): the primary is sttLanguageCode, else ttsLang, else en-US, and the alternatives are the stored list (or the platform default if null). See Channels.
  • <vai-avatar>: sdkStart returns the resolved values, and you pass them to the element as stt-lang and stt-alternative-langs. See Avatar component.
  • Direct /stt calls are unaffected: whatever you send in the request body wins. See Avatar Service REST API.

Opting an agent back into Chinese. Mandarin and Cantonese are no longer in the platform default, because accented English was being misdetected as Chinese. If an agent’s callers really speak Chinese, opt in explicitly:

{ "sttAlternativeLanguageCodes": ["cmn-Hans-CN"] }

Add "yue-Hant-HK" as well for Cantonese (up to 3 entries in total).

Choosing the primary matters more than the alternatives. The speech provider weights the primary language far more heavily than the fallbacks, and testing showed the same audio can produce a clean transcript with the right primary and a corrupted one with the wrong primary. Set sttLanguageCode to the language callers most often speak, not just a placeholder. Adding more alternatives is not a substitute, and every added language slightly raises the chance of a wrong-language match, so list only the languages you expect. A fallback that wins can still be re-checked in the primary only: see the re-check guard.

Accuracy caveat. Fallback detection is best-effort and was tested mainly on synthetic voices. Mixed-language speech within one clip is not handled well (the provider picks one dominant language), and Hokkien and Hakka are not supported. Check the setting against your own real voice traffic before depending on it.

Fetches a single Agent by id, scoped to the caller’s tenant. Returns null if not found or not owned by this tenant (never an error — don’t rely on a thrown exception to detect a missing agent).

Auth: JWT

agent(id: ID!): AgentDto

Request

query GetAgent($id: ID!) {
agent(id: $id) {
id
name
personaPrompt
useCase
status
voiceTier
workflowId
}
}
{ "id": "42" }
POST /graphql
Content-Type: application/json
Authorization: Bearer <jwt>
x-huat-platform: customer

Response

{
"data": {
"agent": {
"id": 42,
"name": "Support Assistant",
"personaPrompt": "You are a helpful support agent for Acme's product line...",
"useCase": "SUPPORT",
"status": "ACTIVE",
"voiceTier": "STANDARD",
"workflowId": 7
}
}
}

Lists every Agent belonging to the caller’s tenant. Returns a plain array, not a Relay connection — there is no cursor pagination or filtering on this field.

Auth: JWT

agents: [AgentDto!]!

Request

query ListAgents {
agents {
id
name
status
useCase
}
}

Response

{
"data": {
"agents": [
{
"id": 42,
"name": "Support Assistant",
"status": "ACTIVE",
"useCase": "SUPPORT"
},
{
"id": 43,
"name": "Onboarding Guide",
"status": "ACTIVE",
"useCase": "ONBOARDING"
}
]
}
}

Total number of Agents belonging to the caller’s tenant (includes only non-soft-deleted agents).

Auth: JWT

agentCount: Int!

Request

query {
agentCount
}

Response

{ "data": { "agentCount": 2 } }

Returns every valid option/source for the Agent config fields that are not plain GraphQL enums: the avatar GLB catalog (proxied live from the avatar service, so this list is always current), a curated subset of recommended TTS voices, and doc URLs for the full external catalogs (Google TTS voices, Google STT languages, TalkingHead lipsync modules) that are too large to mirror inline.

Use this query to discover valid values for avatarGlb, ttsVoice, ttsLang, and lipsyncLang — don’t guess or hardcode these strings. voiceTier, useCase, status, and avatarGender are real enums; introspect them directly instead (see above).

Auth: JWT

agentConfigOptions: AgentConfigOptionsDto!

AgentConfigOptionsDto fields

FieldTypeNotes
avatarModels[AvatarModelDto!]!Each: { id, filename, label, gender }.
recommendedTtsVoices[RecommendedTtsVoiceDto!]!Each: { name, languageCode, gender, tier }.
ttsVoiceCatalogUrlString!Link to the full external Google TTS voice catalog.
sttLanguageCatalogUrlString!Link to the full external Google STT language catalog.
lipsyncModuleDocsUrlString!Link to TalkingHead lipsync module docs.

Request

query GetAgentConfigOptions {
agentConfigOptions {
avatarModels {
id
filename
label
gender
}
recommendedTtsVoices {
name
languageCode
gender
tier
}
ttsVoiceCatalogUrl
sttLanguageCatalogUrl
lipsyncModuleDocsUrl
}
}

Response

{
"data": {
"agentConfigOptions": {
"avatarModels": [
{
"id": "avatar-01",
"filename": "avatar-01.glb",
"label": "Avatar 1",
"gender": "female"
}
],
"recommendedTtsVoices": [
{
"name": "en-US-Neural2-F",
"languageCode": "en-US",
"gender": "female",
"tier": "NEURAL2"
}
],
"ttsVoiceCatalogUrl": "https://cloud.google.com/text-to-speech/docs/voices",
"sttLanguageCatalogUrl": "https://cloud.google.com/speech-to-text/docs/languages",
"lipsyncModuleDocsUrl": "https://docs.talkinghead.example/lipsync-modules"
}
}
}

This agent’s change history — who changed what, when. Only fields that were actually part of an updateAgent call are recorded; a field set to the value it already had is not recorded. Newest first.

Auth: JWT

agentAuditLogs(id: ID!): [AuditLogDto!]!

AuditLogDto fields

FieldTypeNotes
idInt!
auditableTypeString!Always "Agent" for this query.
auditableIdFloat!The agent’s own id.
actionAuditActionEnum!CREATE, UPDATE, or DELETE.
userIdFloatThe acting user’s id at write time — a snapshot, not a live reference.
usernameStringThe acting user’s name at write time — same snapshot caveat.
changesJSONObject{ [field]: { from, to } } for the fields that changed. from and to are null for credential-like fields and on create/delete entries.
createdAtDateTime!

Request

query AgentAuditLogs($id: ID!) {
agentAuditLogs(id: $id) {
id
action
userId
username
changes
createdAt
}
}
Terminal window
curl https://api.wetel.dev/graphql \
-H "Content-Type: application/json" \
-H "x-huat-platform: customer" \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-d '{"query":"query AgentAuditLogs($id: ID!) { agentAuditLogs(id: $id) { id action userId username changes createdAt } }","variables":{"id":"1"}}'

Response

{
"data": {
"agentAuditLogs": [
{
"id": 7,
"action": "UPDATE",
"userId": 42,
"username": "Jane Doe",
"changes": {
"personaPrompt": { "from": "Old prompt", "to": "New prompt" }
},
"createdAt": "2026-09-05T10:00:00.000Z"
}
]
}
}

Creates a new Agent (persona) for the caller’s tenant. Does not create a Session — call startSession with the returned id to begin a conversation (see Sessions).

Auth: JWT

createAgent(input: CreateAgentInput!): AgentDto!

CreateAgentInput fields

FieldTypeRequiredNotes
nameString!yes
personaPromptString!yes
positionString!yes
subjectStringno
openingGreetingStringno
useCaseAgentUseCase!noDefaults to INTERVIEW if omitted.
voiceTierVoiceTier!noDefaults to STANDARD if omitted.
ttsVoiceStringno
ttsLangStringno
sttLanguageCodeStringnoSTT primary language. null/omitted = use ttsLang. See Speech-to-text languages.
sttAlternativeLanguageCodes[String!]noSTT fallback languages, max 3. null/omitted = platform default. See Speech-to-text languages.
speakingRateFloatno
vadThresholdFloatno
avatarGlbStringno
avatarGenderAvatarGenderCodeno
lipsyncLangStringno
evaluationPromptTemplateStringno
evaluationRecommendationLabels[String!]no
externalSyncUrlStringno
externalSyncSecretRefStringno
externalSyncOwnerIdIntno
externalSyncSchemaConfigJSONno
llmProviderOverrideLlmProviderTypenoPilot — see Agents: LLM provider override.
llmModelOverrideStringnoPilot — same section.

Request

mutation CreateAgent($input: CreateAgentInput!) {
createAgent(input: $input) {
id
name
status
useCase
voiceTier
}
}
{
"input": {
"name": "Support Assistant",
"personaPrompt": "You are a helpful support agent for Acme's product line. Keep answers short and friendly.",
"position": "Support Assistant",
"useCase": "SUPPORT",
"voiceTier": "STANDARD",
"ttsVoice": "en-US-Standard-C",
"ttsLang": "en-US"
}
}
POST /graphql
Content-Type: application/json
Authorization: Bearer <jwt>
x-huat-platform: customer

Response

{
"data": {
"createAgent": {
"id": 42,
"name": "Support Assistant",
"status": "ACTIVE",
"useCase": "SUPPORT",
"voiceTier": "STANDARD"
}
}
}

Updates an existing Agent owned by the caller’s tenant. id is a separate argument, not a field inside input. Every field on UpdateAgentInput is optional — send only what is changing; omitted fields are left untouched (this is a partial update, not a full replace).

Auth: JWT

updateAgent(id: ID!, input: UpdateAgentInput!): AgentDto!

UpdateAgentInput fields (all optional)

FieldType
nameString
personaPromptString
positionString
subjectString
openingGreetingString
useCaseAgentUseCase
statusAgentStatus
voiceTierVoiceTier
ttsVoiceString
ttsLangString
sttLanguageCodeString
sttAlternativeLanguageCodes[String!]
speakingRateFloat
vadThresholdFloat
avatarGlbString
avatarGenderAvatarGenderCode
lipsyncLangString
evaluationPromptTemplateString
evaluationRecommendationLabels[String!]
externalSyncUrlString
externalSyncSecretRefString
externalSyncOwnerIdInt
externalSyncSchemaConfigJSON
workflowIdInt
llmProviderOverrideLlmProviderType
llmModelOverrideString

Note that workflowId is only settable via updateAgent, not createAgent — create the Workflow first (see Workflows), then attach it here. Remember a Workflow must be published for a session to actually execute it.

llmProviderOverride/llmModelOverride (pilot — see Agents: LLM provider override) both support detach-to-default: omit the field to leave it unchanged, or send an explicit null to clear it back to “no override.”

Request

mutation UpdateAgent($id: ID!, $input: UpdateAgentInput!) {
updateAgent(id: $id, input: $input) {
id
status
workflowId
}
}
{
"id": "42",
"input": {
"status": "INACTIVE",
"workflowId": 7
}
}

Response

{
"data": {
"updateAgent": {
"id": 42,
"status": "INACTIVE",
"workflowId": 7
}
}
}

Soft-deletes an Agent owned by the caller’s tenant — the row is marked deleted, not removed. Sessions already run against the Agent are unaffected: their history, messages, and Evaluations remain fully readable. A soft-deleted Agent no longer appears in agents/agent(id) results and cannot be used to startSession again.

Auth: JWT

deleteAgent(id: ID!): Boolean!

Request

mutation DeleteAgent($id: ID!) {
deleteAgent(id: $id)
}
{ "id": "42" }

Response

{ "data": { "deleteAgent": true } }

  • Agents — conceptual guide to what an Agent is and how personas map to conversations
  • Sessions — starting and running conversations against an Agent
  • Workflows — attaching a visual conversation graph via workflowId
  • Core API Flow — the end-to-end narrative from Agent creation to a live session
  • Authentication — obtaining the JWT used on every operation on this page
  • API Reference: Overview