config object on the agent document. This page documents every field of it: type, default, bounds, and what the runtime actually does with it.
Field names, defaults, and bounds come from AgentConfigPayload and its nested models in apps/api/app/models/schemas.py. What the runtime reads comes from apps/runtime/services/pipecat/config.py and its neighbours.
config is validated on both POST /api/v1/agents and PATCH /api/v1/agents/{agent_id} by validate_agent_config() in apps/api/app/services/agent_config_validation.py. A failure returns 422. The whole page is also live on a running API at /docs.The config object
AgentConfigPayload has seven fields.
behaviour and knowledge_base may be omitted entirely and take their model defaults. prompts, language, and models are required.
prompts
AgentPrompts. Two fields.
validate_agent_config() strips whitespace from greeting_message and rejects an empty result with prompts.greeting_message is required. Both strings pass through variable substitution — see Custom variables.
behaviour
AgentBehaviour. Every field is optional and every field has a default, so "behaviour": {} is valid. The description= column is the text from the model itself.
How the runtime reads them
The Pydantic defaults are not always the effective defaults.pipeline_config_from_behaviour() and online_detection_from_behaviour() coerce nulls when they build the pipeline:
The idle timeout the pipeline uses is
user_online_detection_seconds when online detection is enabled, and user_silence_hangup_seconds otherwise (PipelineConfig.user_idle_timeout). The two settings share one timer — you cannot have both.
ignore_user_speech_before_greeting installs Pipecat’s MuteUntilFirstBotCompleteUserMuteStrategy. interruption_min_words above zero installs a MinWordsUserTurnStartStrategy.
Hold messages need both halves: hold_from_behaviour() returns nothing unless hold_message_timeout_seconds is non-null and greater than zero and hold_messages contains at least one non-blank string. One message is chosen at random per inference, and only one is played per turn.
Online detection speaks user_online_detection_message up to user_online_detection_repeats times; on the next idle it speaks user_online_detection_closing_message and ends the call. With detection disabled, a single idle timeout speaks the closing message and ends the call directly.
automatic_call_ending registers an end_conversation function tool on the LLM context, but only when both enabled and graceful_llm_call_ending are true — _call_ending_enabled() in apps/runtime/services/pipecat/call_ending.py requires the pair. The tool ends the call when the model calls it.
call_timeout_seconds is accepted, validated, and stored, but nothing in apps/runtime reads it. There is no hard call-duration limit in the pipeline. Enforce a ceiling at your telephony provider if you need one.Example from the model
AgentBehaviour carries a json_schema_extra example, reproduced here verbatim from apps/api/app/models/schemas.py:
language
AgentLanguage. Two fields.
Valid ids come from
GET /api/v1/languages, which returns the canonical id → label map the agent builder uses. The per-provider language filter is GET /api/v1/configuration/stt?languages= and its TTS equivalent.
secondary is stored on the agent and returned by the API, but no code reads it. There is no mid-call language switching in VoicEra: nothing in apps/runtime inspects language.secondary, and no processor swaps the STT or TTS language once a session is running. The language a call runs in is whatever the stt_config and tts_config were built with. Treat secondary as documentation of intent, not as behaviour.models
AgentModels. Three required objects, one per pipeline stage.
Two rules apply to all three, enforced by
validate_persisted_model_config():
provideris required and must be registered. It is matched against the provider registry for that kind; an unknown id fails validation with the registry’s own error. Enumerate the valid ids withGET /api/v1/configuration/stt,/tts, and/llm, and fetch one provider’s setting schema fromGET /api/v1/configuration/{kind}/setting/{provider}.- Non-secret settings only. Every auth and secret field name declared by the provider’s config class is forbidden. Sending one fails with
{kind}_config must not include secret/auth fields: …. API keys live inProviderAuth, stored once per organisation throughPOST /api/v1/authand Fernet-encrypted at rest. See Provider credentials (ProviderAuth).
null values dropped.
knowledge_base
AgentKnowledgeBase. Optional; the default is disabled.
When
enabled is true, validation requires at least one non-blank document_ids entry, and — when the request carries an organisation — asserts every named document is ready, not still processing or failed.
mode: "tool" additionally requires an LLM provider that supports function calling. The allowed set is KB_TOOL_LLM_PROVIDERS in apps/api/app/services/agent_config_validation.py: anthropic, azure_openai, groq, openai. Any other provider is rejected.
The runtime re-parses this blob in apps/runtime/services/knowledge/config.py and is more forgiving than the API: an unrecognised mode falls back to context, and top_k is clamped into 1–10. Knowledge is skipped entirely when enabled is false or document_ids is empty. See Knowledge base (RAG).
Custom variables and prompt substitution
custom_variables is a free-form object of named defaults. Keys must be non-empty strings; values are arbitrary.
At call time the runtime merges two layers, with the call winning:
resolve_custom_variables() in apps/runtime/services/pipecat/audio.py. Per-call values arrive from the custom_variables field on POST /api/v1/calls/outbound, or from a campaign contact row’s context_variables.
Substitution is applied to both system_prompt and greeting_message before the pipeline starts. The syntax is {{variable_name}}, matched by the regular expression \{\{(\w+)\}\} — word characters only, so no dots, dashes, or spaces in a name.
substitute_variables(), both covered by apps/runtime/tests/test_prompt_substitution.py:
- A missing key becomes an empty string, not the literal placeholder.
"Hi {{name}}"with nonamerenders as"Hi ". Declare a default incustom_variablesfor every placeholder you use. - Non-string values are coerced with
str(). Numbers and booleans render as their Python representation.
A complete example
The example below isAgentCreateRequest.config as declared in apps/api/app/models/schemas.py, unaltered: