Beyond prompt text, an avatar carries a small group of behaviour options and an optional set of tools it can call. Both follow the same defaults-vs-overrides rule as the prompt: stored on the profile, overridable per session.
Structured options that shape session behaviour without being prompt text. Stored as the profile’s
config_json, overridable at mint with config.
The namespace is curated: unknown keys are rejected, both when authoring a profile and at mint.
| Key | Type | Default | Range | What it does |
|---|---|---|---|---|
greeting_enabled |
boolean | true |
— | Whether the avatar speaks a proactive opener when the session starts |
greeting_hold_ms |
integer | 2000 |
0–5000 |
How long the box waits for your session context to arrive before the first spoken turn. 0 disables the hold |
The defaults are descriptive — they document what the GPU fleet does when a key is not set. Setting a key is what overrides the fleet-wide behaviour.
greeting_hold_ms in practiceWhen a session carries overrides (context.system_prompt, extra_system_prompt, a profile that
differs from the sealed baseline), that text reaches the box a moment after the connection opens.
The hold makes the box wait — up to this many milliseconds — before generating its first spoken
turn, so the opener reflects your context instead of the avatar’s baseline persona.
0) if you want the fastest possible first word and the opener is generic.curl -sS -X POST https://api.casola.ai/api/v1/avatars/019fe463-…/profiles \
-H "Authorization: Bearer $CASOLA_SECRET_KEY" \
-H "content-type: application/json" \
-d '{
"system_prompt": "You are Lyra, Acmes product guide.",
"config_json": { "greeting_enabled": false },
"set_default": true
}'
Config versions with the profile and is part of its hash — changing only a config value produces a genuinely new profile.
{
"avatar_id": "019fe463-…",
"config": { "greeting_enabled": false }
}
Config merges per key, not wholesale:
session config[key] ?? profile config_json[key] ?? GPU fleet default
A profile with {greeting_enabled: false, greeting_hold_ms: 3000} plus a session
{greeting_hold_ms: 0} runs with greeting_enabled: false and greeting_hold_ms: 0. Sending
one key never clears the others.
| Response | Cause |
|---|---|
400 invalid_config + detail: "unknown_config_key:foo" |
Key not in the catalog |
400 invalid_config + detail: "config_not_boolean:greeting_enabled" |
Wrong type |
400 invalid_config + detail: "config_out_of_range:greeting_hold_ms" |
Outside the documented range |
400 invalid_config + detail: "config_too_large" |
Over 1,024 serialized bytes |
403 tools_context_not_allowed |
Sent from a publishable/device/issuer credential |
Profile authoring returns 400 invalid_config_json with the same detail vocabulary.
null for a key means “unset” and is dropped, so {}, {greeting_enabled: null} and an absent
config are all identical and hash the same.
Tools let the avatar call back into your systems mid-conversation. They resolve on the same ladder as the prompt text.
1. mint `tools` — replaces every lower layer wholesale
2. profile `tools_json` — the avatar's stored default
3. version `tools_json` — the sealed baseline
+ mint `extra_tools` — merged in on top of whatever won
{
"inline": [
{
"name": "lookup_order",
"description": "Look up an order by its id and return status and ETA.",
"input_schema": {
"type": "object",
"properties": { "order_id": { "type": "string" } },
"required": ["order_id"]
}
}
],
"servers": ["mcp_billing"],
"allow": ["lookup_order"],
"max_calls": 20,
"history": true
}
| Field | Meaning |
|---|---|
inline |
Tool definitions declared here. Max 16 tools; description ≤ 300 chars; names must be unique |
servers |
References to MCP servers registered in your workspace. Max 4 |
allow |
Allowlist filter over everything resolved — useful to narrow a stored set for one session |
max_calls |
Tool calls per session. Default 20, max 100 |
history |
Whether conversation history is sent to your tool endpoint |
Any other key is 400 unknown_tools_field:<key>. Tool calls are delivered to the agent gateway
endpoint configured for your workspace; wiring that endpoint is outside this document’s scope.
{
"tools_json": { "inline": [ { "name": "lookup_order", "description": "…", "input_schema": { … } } ] },
"set_default": true
}
// Replace: only this tool, whatever the profile says
{ "avatar_id": "019fe463-…", "tools": { "allow": ["lookup_order"], "inline": [ … ] } }
// Add: the avatar's stored tools PLUS one more for this session
{ "avatar_id": "019fe463-…", "extra_tools": { "inline": [ { "name": "escalate_ticket", … } ] } }
The mint response echoes what resolved:
{ "status": "ready", "tools_resolved": ["lookup_order", "escalate_ticket"], … }
If tools_resolved is absent, the session has no tools.
A malformed stored tools_json is never fatal to a session: the mint runs with no tools rather
than failing a live call. A malformed sent tools is a 400 — it is your input, this request.
That is the same asymmetry as the prompt caps, for the same reason: authoring time is where you
should learn about bad configuration, not connect time.
Not part of the config group, but set per session on the same mint:
| Parameter | Type | Notes |
|---|---|---|
response_language |
string | "en" or "English". Adds a [Language] preference to the prompt. "" = unset |
session_cap_seconds |
integer | 60–1200. Hard limit the box enforces. Secret keys only |
observability |
boolean | Default true. false records nothing for this session |
domain_allowlist |
string[] | Origins the edge accepts the browser connection from |
metadata |
object | Opaque, echoed in your session records |
response_language is a preference, not a lock: the [Core] rule “mirror the user’s spoken
language” still applies, so a user who speaks Japanese to a response_language: "en" avatar gets a
sensible answer rather than a mismatch.
observability: false disables session capture for that session. If you record sessions, your own
terms must disclose it to your end users — see the Session capture section of the Developer Terms.
Next: 6. Error reference →