Bot configuration reference
This chapter lists every key a bot accepts, with its type, default, allowed values and what LiteBot does with it. Use it when you write a bot JSON file or a POST /api/bots body, or when a bot is skipped at load. How bots are loaded and edited is explained in Bots.
How to read this reference
The authority is the Joi schema botConfigSchema in src/core/bot-manager.js. Every bot, from a file, from Mongo or from the API, passes through it.
- A key that is not listed here is rejected, and the bot is skipped or the API call fails with 400 (
"<key>" is not allowed). - "Default" is the value Joi fills in when you leave the key out. "none" means the key stays absent.
- "Runtime effect" says what reads the key. Some keys are stored and returned by the API but change nothing at runtime; the tables say so.
- A nested object with a default of
{}gets its inner defaults only when you include the object. Left out, it stays{}.
Identity and tenancy
| Key | Type | Default | Meaning |
|---|---|---|---|
id |
string, required | none | Unique bot id. Used in chat requests (botId), file names (<id>.json, <id>.txt) and every analytics record. Cannot be changed by PUT |
name |
string, required | none | Display name. Also used in the generic handoff message and the flow-bot prompt |
description |
string | none | Shown in lists. Flow bots put it in each step prompt (You are <name>. <description>.) |
tenantId |
string matching ^[a-z0-9][a-z0-9-]{1,39}$ |
default |
Owning tenant. Scopes dashboard access, tool resolution and tenant LLM keys. On the API a tenant user's bots are always in their own tenant |
isActive |
boolean | true |
false hides the bot from lists, and chat to it fails with Bot is not active. Mongo bots with false are not loaded at all |
Model and provider
| Key | Type | Default | Meaning |
|---|---|---|---|
provider |
string | groq |
LLM provider name: groq, openrouter, openai, gemini, gemini-vertex, sarvam, ollama. The schema accepts any string; an unconfigured name fails at chat time. See LLM providers |
model |
string | none | Model id for that provider. Unset uses the provider's default model |
temperature |
number, 0–2 | 0.7 |
Sampling temperature sent to the provider |
maxTokens |
number, 1–8000 | 1000 |
Output token limit for each model call |
Set provider explicitly. The groq default fails on a server without a Groq key.
Prompt and variables
| Key | Type | Default | Meaning |
|---|---|---|---|
systemPrompt |
string, required | none | The system prompt. For a file bot it may be omitted from the JSON when config/prompts/<id>.txt exists; the loader fills it in before validation |
variables |
object of string, number or boolean values | {} |
Values for {{name}} placeholders. Replaced at file load and at voice session start, not for Mongo or API bots in text chat. See Variables |
injectContext |
boolean | true |
Adds the session context (keys not starting with _) as a Current session context: system message on each text turn. Voice sessions add caller context to the prompt when this is true |
language |
string | en |
Stored and listed only. Not sent to the model; put language rules in the prompt |
responseFormat |
object | { "type": "text" } |
Stored only. No runtime effect |
responseFormat.type |
text, json, markdown |
text |
Stored only |
responseFormat.schema |
object | none | Stored only |
Tools
| Key | Type | Default | Meaning |
|---|---|---|---|
tools |
array of strings | [] |
Tool names. In text chat an empty list offers every tool visible to the tenant; in voice it offers none. Unknown names are dropped. See Tools and Tools |
Routing and handoff
These keys drive the keyword intent router and in-session bot switching described in Routing and bot switching.
| Key | Type | Default | Meaning |
|---|---|---|---|
intentThreshold |
number, 0–1 | 0.3 |
Minimum score for this bot to be picked for a new session sent without botId. 0 is treated as 0.3 |
switchingThreshold |
number, 0–1 | 0.4 |
Minimum score another bot needs to take a session away from this bot. 0 is treated as 0.4. Set 1 to make switching away rare |
handoffMessages |
object, bot id → string | {} |
Message added when this bot hands a session to the named bot. Without an entry a generic message names the target's department or name |
Metadata
metadata is a free-form object (default {}): any keys are accepted. These are read by code:
| Key | Type | Default | Meaning |
|---|---|---|---|
metadata.keywords |
array of strings | none | Intent-router keywords. Phrase match adds 0.8, each word match 0.3. A bot without keywords or patterns is never routed or switched to |
metadata.patterns |
array of regex strings | none | Case-insensitive patterns; each match adds 0.7 |
metadata.priority |
number | none | Adds priority / 10 to the bot's score when it has at least one match |
metadata.department |
string | none | Shown in analytics and intent results; used in the generic handoff message |
metadata.isVisualBuilder |
boolean | none | With a non-empty _visualFlow, makes this a flow bot (Visual flows) |
metadata.flowVersion |
string | none | Written by the visual editor; informational |
Escalation
escalationRules (default {}) decides when a reply carries shouldEscalate: true. LiteBot only sets the flag; see Escalation rules.
| Key | Type | Default | Meaning |
|---|---|---|---|
escalationRules.keywords |
array of strings | [] |
Escalate when the user message contains one (case-insensitive) |
escalationRules.maxAttempts |
number, ≥ 1 | 3 when escalationRules is present |
Escalate when the session holds maxAttempts × 2 stored messages. Absent when escalationRules is left out |
escalationRules.sentimentThreshold |
number, -1 to 1 | none | Accepted, not used |
A tool result with escalated: true also sets the flag, whatever the rules say. Flow bots never set it.
Session-end hooks
onSessionEnd (default []) lists tools run once when the session ends, without a model call. Details and template values are in Session-end hooks.
| Key | Type | Default | Meaning |
|---|---|---|---|
onSessionEnd[].tool |
string, required | none | Tool name |
onSessionEnd[].params |
object | {} |
Tool parameters. Strings may use {{sessionId}}, {{botId}}, {{userId}}, {{reason}}, {{messageCount}}, {{transcript}} and {{context.<key>}} |
onSessionEnd[].reasons |
array of remote_hangup, transfer, llm_hangup, idle_timeout, api |
none | Run only for these end reasons. Absent or empty runs for every reason |
Voice
voice is optional. Without it, or with voice.enabled not true, a /voice session for the bot fails with VOICE_NOT_ENABLED. When you include voice, the defaults below are filled in. These keys apply only to Gemini Live sessions on /voice (Voice).
| Key | Type | Default | Meaning |
|---|---|---|---|
voice.enabled |
boolean | false |
Allows /voice sessions for this bot |
voice.model |
string | GEMINI_LIVE_MODEL at start-up, else gemini-3.1-flash-live-preview |
Gemini Live model. A models/ prefix is added if missing |
voice.voiceName |
string | Aoede |
Gemini prebuilt voice |
voice.languageCode |
string | en-US |
Speech language sent to Gemini |
voice.responseModality |
AUDIO, TEXT |
AUDIO |
Reply type when the client does not send mode |
voice.speechConfig.speakingRate |
number, 0.25–4.0 | 1.0 |
Accepted but not sent to Gemini Live: no effect |
voice.speechConfig.pitch |
number, -20–20 | 0.0 |
Accepted but not sent to Gemini Live: no effect |
voice.interruption.enabled |
boolean | true |
false stops the caller from cutting the bot off (NO_INTERRUPTION) |
voice.turnCoverage |
TURN_INCLUDES_ONLY_ACTIVITY, TURN_INCLUDES_ALL_INPUT |
none | What audio counts as the user turn. Unset uses VOICE_TURN_COVERAGE, then Gemini's default (only activity) |
voice.inputMimeType |
string matching audio/pcm;rate=<n> |
none | Input audio format. Unset uses VOICE_INPUT_MIME_TYPE, then audio/pcm;rate=16000 |
voice.vad
Voice activity detection. Each field falls back to its environment variable, then to Gemini's own default.
| Key | Type | Default | Meaning |
|---|---|---|---|
voice.vad.silenceDurationMs |
integer, 100–5000 | VOICE_VAD_SILENCE_MS |
Silence that ends the caller's turn |
voice.vad.prefixPaddingMs |
integer, 0–5000 | VOICE_VAD_PREFIX_PADDING_MS |
Audio kept before detected speech |
voice.vad.startOfSpeechSensitivity |
START_SENSITIVITY_LOW, START_SENSITIVITY_HIGH, START_SENSITIVITY_UNSPECIFIED |
VOICE_VAD_START_SENSITIVITY |
How easily speech start is detected |
voice.vad.endOfSpeechSensitivity |
END_SENSITIVITY_LOW, END_SENSITIVITY_HIGH, END_SENSITIVITY_UNSPECIFIED |
VOICE_VAD_END_SENSITIVITY |
How easily speech end is detected |
voice.vad.disabled |
boolean | VOICE_VAD_DISABLED |
true turns off automatic detection; the other VAD fields are then not sent and the client marks turns |
Visual flow
| Key | Type | Default | Meaning |
|---|---|---|---|
_visualFlow |
object | none | Flow graph saved by the visual editor |
_visualFlow.nodes |
array of objects, required | none | Nodes. Contents are not checked by the schema |
_visualFlow.edges |
array of objects, required | none | Edges. Contents are not checked by the schema |
_visualFlow.metadata |
object | none | Editor timestamps and version |
A bot runs as a flow bot only when _visualFlow.nodes is non-empty and metadata.isVisualBuilder is true. Node types and runtime behaviour are in Visual flows.
Keys the loader reads but the schema rejects
| Key | Read by | What happens |
|---|---|---|
promptFile |
File loader (would load config/prompts/<file>) |
The bot is skipped with "promptFile" is not allowed. Use config/prompts/<id>.txt instead |
voice.thinkingBudget |
Voice handler | The bot is skipped with "voice.thinkingBudget" is not allowed. Set VOICE_THINKING_BUDGET instead; the default is 0 (thinking off) |
Complete annotated example
{
"id": "clinic-reception",
"name": "Clinic Reception",
"description": "Front desk for Example Clinic",
"tenantId": "default",
"isActive": true,
"provider": "gemini-vertex",
"model": "gemini-2.5-flash",
"temperature": 0.3,
"maxTokens": 600,
"systemPrompt": "You are the receptionist for {{clinicName}}. Reply in at most three sentences. For bookings, collect name and preferred day, then call bookAppointment.",
"variables": { "clinicName": "Example Clinic" },
"injectContext": true,
"language": "en",
"tools": ["getSessionContext", "bookAppointment", "hangupCall"],
"escalationRules": { "keywords": ["speak to a human", "complaint"], "maxAttempts": 100 },
"intentThreshold": 0.3,
"switchingThreshold": 1,
"handoffMessages": {},
"metadata": { "department": "reception", "keywords": ["appointment", "clinic hours"] },
"onSessionEnd": [
{
"tool": "sendCallSummary",
"params": { "session": "{{sessionId}}", "caller": "{{context.caller_id}}", "transcript": "{{transcript}}" },
"reasons": ["remote_hangup", "llm_hangup", "idle_timeout"]
}
],
"voice": {
"enabled": true,
"voiceName": "Aoede",
"languageCode": "en-IN",
"interruption": { "enabled": true },
"vad": { "silenceDurationMs": 700 },
"inputMimeType": "audio/pcm;rate=16000"
}
}
Notes on the example:
providerandmodelare set explicitly instead of relying on thegroqdefault.{{clinicName}}is replaced when the file loads. If you create this bot through the API instead, write the name into the prompt, because text chat does not replace variables for API bots.toolsis listed explicitly. An empty list would offer every tool on the server in text chat.maxAttempts: 100keepsshouldEscalatefor keywords and tools only.switchingThreshold: 1keeps callers on this bot.metadata.keywordsstill lets an unpinned new session be routed here.onSessionEndsends a summary when the call ends for any reason except a transfer or an API end. The tool name is an example; use one from your registry.voiceleavesmodelout, so it usesGEMINI_LIVE_MODEL, and leaves outthinkingBudget, which the schema rejects.
Where to go next
- Loading, creating and editing bots: Bots
- Provider names and models: LLM providers
- Writing the tools a bot lists: Tools
- Voice sessions and the
VOICE_*variables: Voice and Environment reference, part 2