# Create an agent (/en/api-reference/api-ref/conversational-ai/platform/create-agent)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

> Documentation: Conversational AI API.
> Documentation version: Preview.
> This document describes only this version. [Version index](/llms/doc-versions-convoai-api-v2.txt).


Saves a reusable agent configuration in your project.

- OpenAPI: /openapi/conversational-ai/rest-api-platform.en.yaml
- Operation ID: create-agent
- Method: POST
- Path: /v1/projects/{appid}/agents
- Endpoint: https://preview.ai.agora.io/conversational-ai/v1/projects/{appid}/agents

## Servers

- https://preview.ai.agora.io/conversational-ai

Use this endpoint to save an agent that you can then use to start sessions and campaigns. Only `agent_name` is required. If you omit `greeting`, the agent doesn't greet participants automatically.

Saving or updating an agent doesn't change sessions that are already running.


:::info[Keep provider keys in secrets]
Set `credential` on each pipeline stage. With `byok`, pass your provider keys in `credential.fields` as `$secrets.<set>.<key>` references to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). Most providers take one field, `api_key`. Agora checks that each reference exists when you create the agent. Keep credentials out of `params`.
:::

## Authorization

This endpoint requires authentication.

- `tokenAuth`
- `basicAuth`

## Parameters

- `appid` (path, required, string) - The App ID of your project.

## Request body

- `agent_name` (string, required) - A name for the agent, 1 to 255 characters. It doesn't need to be unique.
  - Min length: `1`
  - Max length: `255`
- `instructions` (string) - The system instructions that define how the agent behaves. The model receives them as the first `system` message, before `messages`.
- `messages` (array) - Messages that start the conversation history. They follow `instructions`.
  - `messages.items` (object)
    - `messages.items.role` (string, required) - The author of the message:

- `system`: An instruction to the model.
- `user`: A message from the user.
- `assistant`: A message from the agent.
      - Allowed: `system` | `user` | `assistant`
    - `messages.items.content` (string, required) - The text of the message.
- `variables` (object) - Every variable the agent uses, keyed by variable name. A name starts with a lowercase letter and contains up to 64 lowercase letters, digits, and underscores. Reference a variable as `{{variable_name}}` in `instructions`, `messages`, `greeting.text`, `pipeline.llm.failure_message`, `pipeline.silence_config.content`, and generated filler instructions. The agent doesn't store values: pass them in `values` when you start a session, or in each campaign contact. Expansion is single-pass, so a value that contains a placeholder isn't expanded again.
  - Example: `{"customer_name":{"type":"str","required":true},"resolution_status":{"type":"str","options":["resolved","follow_up","unresolved"],"extract":true,"description":"Outcome of the conversation."}}`
- `greeting` (object) - The message the agent speaks when a participant joins. If you omit it, the agent doesn't greet participants automatically.
  - `greeting.mode` (string, required) - `text`: The agent speaks `text` using the TTS stage.
  - `greeting.on` (string) - Which participants the agent greets:

- `each_join`: Every participant who joins.
- `first_join`: Only the first participant to join.
    - Allowed: `each_join` | `first_join`
    - Default: `each_join`
  - `greeting.delay_ms` (integer) - The delay in milliseconds between a participant joining and the agent starting the greeting.
    - Default: `0`
    - Range: `[0, 5000]`
  - `greeting.text` (string, required) - The greeting text.
- `pipeline` (object) - The models that power the agent: separate ASR, LLM, and TTS stages. When you pass a pipeline, `llm` and `tts` are required.
  - `pipeline.mode` (string, required) - `cascaded`: Separate ASR, LLM, and TTS stages.
  - `pipeline.asr` (object) - The automatic speech recognition (ASR) stage, which transcribes the user's speech.
    - `pipeline.asr.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.asr.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.asr.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.asr.vendor` (string) - The ASR provider, for example `deepgram`.
    - `pipeline.asr.model` (string) - The provider model, for example `nova-3`.
    - `pipeline.asr.language` (string) - The language of the user's speech, for example `en-US`.
    - `pipeline.asr.url` (string) - A custom provider endpoint. Don't also put the endpoint in `params`.
    - `pipeline.asr.params` (object) - Provider-specific settings that aren't secret, such as `keywords`, passed to the provider as they are. Keep API keys in `credential`, not here.
  - `pipeline.llm` (object, required) - The large language model (LLM) stage, which generates the agent's replies.
    - `pipeline.llm.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.llm.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.llm.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.llm.vendor` (string) - The LLM provider, for example `openai`.
    - `pipeline.llm.style` (string) - The API style used to communicate with the model, for example `openai` for OpenAI and OpenAI-compatible APIs.
    - `pipeline.llm.model` (string) - The provider model.
    - `pipeline.llm.url` (string) - A custom provider endpoint. Don't also put the endpoint in `params`. For Google Gemini in an ephemeral session, the URL can include your API key as `key=<your_api_key>`, with `credential.fields` left out. A saved agent's `url` can't contain a key.
    - `pipeline.llm.headers` (object) - Custom HTTP headers sent to the provider endpoint, at most 32. They are write-only and are never returned. A value can be a whole `$secrets.<set>.<key>` reference when the stage uses `byok` and an approved provider endpoint. Agora resolves it when each session starts. A header name that looks like a credential, such as `X-Api-Key`, must use a reference. Transport, identity, and provider authentication headers are reserved. Set provider authentication in `credential`.
    - `pipeline.llm.params` (object) - Provider-specific settings that aren't secret, such as `reasoning_effort`, passed to the provider as they are. Keep API keys in `credential`, not here.
    - `pipeline.llm.input_modalities` (array) - The input types sent to the model, for example `["text"]`.
      - `pipeline.llm.input_modalities.items` (string)
    - `pipeline.llm.output_modalities` (array) - The output types requested from the model, for example `["text"]`.
      - `pipeline.llm.output_modalities.items` (string)
    - `pipeline.llm.max_history` (integer) - The number of recent conversation messages the LLM keeps as context. It counts messages, not user and agent pairs. The agent's `instructions` don't count toward this limit.
      - Default: `32`
      - Range: `[1, 1024]`
    - `pipeline.llm.failure_message` (string) - The message the agent speaks with the TTS stage when the LLM fails to produce a response.
    - `pipeline.llm.tools` (array) - Function tools the model can call. An agent can have up to 32 tools in total, including tools discovered from `mcp_servers`. Tool names must be unique, ignoring case.
      - Max items: `32`
      - `pipeline.llm.tools.items` (object)
        - `pipeline.llm.tools.items.type` (string, required) - Always `function`.
        - `pipeline.llm.tools.items.name` (string, required) - The tool name shown to the model. It starts with a letter and contains only letters and digits, up to 64 characters. It can't start with `mcp`, can't be `ragsearch_tool`, and can't match an MCP, built-in, or model-native tool.
          - Max length: `64`
        - `pipeline.llm.tools.items.description` (string, required) - What the tool does, 1 to 1,024 characters. The model uses it to decide when to call the tool.
          - Min length: `1`
          - Max length: `1024`
        - `pipeline.llm.tools.items.parameters` (object, required) - A JSON Schema that describes the tool's arguments.
        - `pipeline.llm.tools.items.execution` (object, required) - How the agent runs the tool.
          - `pipeline.llm.tools.items.execution.mode` (string, required) - `sync`: The agent waits for the tool response before it continues.
        - `pipeline.llm.tools.items.server` (object, required) - The HTTP endpoint the agent calls to run the tool.
          - `pipeline.llm.tools.items.server.method` (string, required) - The HTTP method.
            - Allowed: `GET` | `POST`
          - `pipeline.llm.tools.items.server.url` (string, required) - The HTTPS URL of the endpoint, up to 2,048 characters.
            - Min length: `1`
            - Max length: `2048`
            - Pattern: `^https://`
          - `pipeline.llm.tools.items.server.headers` (object) - HTTP headers sent to the endpoint, up to 32. They are write-only and are never returned.
          - `pipeline.llm.tools.items.server.body` (object) - Additional fields to include in the request body.
          - `pipeline.llm.tools.items.server.timeout_ms` (integer) - The request timeout in milliseconds.
            - Default: `10000`
            - Range: `[1000, 100000]`
    - `pipeline.llm.mcp_servers` (array) - MCP servers whose tools the model can call.
      - `pipeline.llm.mcp_servers.items` (object)
        - `pipeline.llm.mcp_servers.items.name` (string, required) - A name for the MCP server, 1 to 48 letters and digits.
          - Min length: `1`
          - Max length: `48`
        - `pipeline.llm.mcp_servers.items.endpoint` (string, required) - The HTTPS URL of the MCP server, up to 2,048 characters.
          - Min length: `1`
          - Max length: `2048`
          - Pattern: `^https://`
        - `pipeline.llm.mcp_servers.items.transport` (string, required) - The MCP transport. `streamable_http`: Streamable HTTP.
        - `pipeline.llm.mcp_servers.items.headers` (object) - HTTP headers sent to the MCP server, up to 32. They are write-only and are never returned.
        - `pipeline.llm.mcp_servers.items.allowed_tools` (array) - The tools the model can call from this server:

- Omit the field, or include `*` in the list, to allow every tool.
- Pass an empty list to allow no tools.
- Otherwise, only the listed tools are allowed.
          - `pipeline.llm.mcp_servers.items.allowed_tools.items` (string)
            - Min length: `1`
        - `pipeline.llm.mcp_servers.items.timeout_ms` (integer) - The request timeout in milliseconds.
          - Range: `[1000, 100000]`
  - `pipeline.tts` (object, required) - The text-to-speech (TTS) stage, which converts the agent's replies to speech.
    - `pipeline.tts.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.tts.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.tts.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.tts.vendor` (string) - The TTS provider, for example `microsoft`.
    - `pipeline.tts.model` (string) - The TTS model the agent uses. Returned in responses only.
    - `pipeline.tts.url` (string) - A custom provider endpoint. Don't also put the endpoint in `params`.
    - `pipeline.tts.headers` (object) - Custom HTTP headers sent to the provider endpoint, at most 32. They are write-only and are never returned. A value can be a whole `$secrets.<set>.<key>` reference when the stage uses `byok` and an approved provider endpoint. Agora resolves it when each session starts. A header name that looks like a credential, such as `X-Api-Key`, must use a reference. Transport, identity, and provider authentication headers are reserved. Set provider authentication in `credential`.
    - `pipeline.tts.params` (object) - Provider-specific settings that aren't secret, such as the voice and, where the provider requires it, the model, passed to the provider as they are. Keep API keys in `credential`, not here.
    - `pipeline.tts.skip_patterns` (array) - Bracketed text that the agent doesn't speak, such as tone or action notes in the LLM
output:

- `fullwidth_parentheses`: Text in `（）`.
- `lenticular_brackets`: Text in `【】`.
- `parentheses`: Text in `( )`.
- `square_brackets`: Text in `[ ]`.
- `curly_braces`: Text in `{ }`.
      - `pipeline.tts.skip_patterns.items` (string)
        - Allowed: `fullwidth_parentheses` | `lenticular_brackets` | `parentheses` | `square_brackets` | `curly_braces`
  - `pipeline.turn_detection` (object) - How the agent detects when the user starts and stops speaking, and handles interruptions.
    - `pipeline.turn_detection.speech_threshold` (number) - The speech detection threshold. Lower values detect speech more easily. Higher values ignore weak speech.
      - Default: `0.5`
      - Range: `[0, 1]`
    - `pipeline.turn_detection.start_of_speech` (object) - How the agent detects that the user starts speaking. Pass the fields of one detection type only. If you omit it, the agent uses `semantic` detection with its defaults.
      - `pipeline.turn_detection.start_of_speech.type` (string, required) - `semantic`: Detects a meaningful start of speech. If you omit `type`, the agent uses `semantic`.
        - Default: `semantic`
      - `pipeline.turn_detection.start_of_speech.interrupt_duration_ms` (integer) - Speech shorter than this, in milliseconds, is ignored.
        - Default: `160`
        - Range: `[120, 1200]`
      - `pipeline.turn_detection.start_of_speech.speaking_interrupt_duration_ms` (integer) - How long in milliseconds the user must speak to interrupt the agent while it's speaking.
        - Default: `160`
      - `pipeline.turn_detection.start_of_speech.prefix_padding_ms` (integer) - How many milliseconds of audio before the detected start of speech to keep, so the beginning of the speech isn't clipped.
        - Default: `800`
        - Range: `[0, 5000]`
    - `pipeline.turn_detection.end_of_speech` (object) - How the agent detects that the user has finished speaking. Pass the fields of one detection type only. If you omit it, the agent uses `semantic` detection with its defaults.
      - `pipeline.turn_detection.end_of_speech.type` (string, required) - `semantic`: Considers whether the utterance sounds complete. If you omit `type`, the agent uses `semantic`.
        - Default: `semantic`
      - `pipeline.turn_detection.end_of_speech.silence_duration_ms` (integer) - The silence in milliseconds that ends the turn.
        - Default: `320`
        - Range: `[120, 2000]`
      - `pipeline.turn_detection.end_of_speech.max_wait_ms` (integer) - The maximum time in milliseconds to wait before ending the turn, from 500 to 10,000. Set to `-1` to wait without a timeout.
        - Default: `3000`
        - Range: `[500, 10000]`
      - `pipeline.turn_detection.end_of_speech.pause_state_enabled` (boolean) - Whether the agent detects a pause within an utterance, such as "hold on", and waits for more input.
        - Default: `true`
    - `pipeline.turn_detection.interruption` (object) - How the user can interrupt the agent while it speaks or thinks. Pass the fields of one interruption mode only. If you omit it, any user speech interrupts the agent.
      - Default: `{"mode":"start_of_speech"}`
      - `pipeline.turn_detection.interruption.mode` (string, required) - `start_of_speech`: Any user speech interrupts the agent.
      - `pipeline.turn_detection.interruption.when_off` (string) - What happens to user speech that arrives while the agent speaks or thinks:

- `append`: The agent responds to the speech after the current reply.
- `ignore`: The agent discards the speech.
        - Allowed: `append` | `ignore`
        - Default: `append`
      - `pipeline.turn_detection.interruption.keywords` (array, required) - The phrases that interrupt the agent, 1 to 128.
        - Min items: `1`
        - Max items: `128`
        - `pipeline.turn_detection.interruption.keywords.items` (string)
          - Min length: `1`
  - `pipeline.silence_config` (object) - What the agent does after a period of silence. This only prompts the user. To hang up a phone call after silence, use `call_policy.max_silence_duration_ms`.
    - `pipeline.silence_config.enabled` (boolean, required) - Whether to act on silence.
    - `pipeline.silence_config.timeout_ms` (integer, required) - How long the silence lasts, in milliseconds, before the agent acts, up to 60,000. Applies only when `enabled` is `true`.
      - Range: `[0, 60000]`
    - `pipeline.silence_config.action` (string, required) - What the agent does when the timeout is reached:

- `speak`: Speaks `content` as written.
- `think`: Gives `content` to the LLM as an instruction, and responds to it.
      - Allowed: `speak` | `think`
    - `pipeline.silence_config.content` (string, required) - The text to speak, or the instruction for the LLM, depending on `action`.
  - `pipeline.avatar` (object) - The avatar that represents the agent on video.
    - `pipeline.avatar.enabled` (boolean) - Whether to show the avatar.
      - Default: `false`
    - `pipeline.avatar.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.avatar.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.avatar.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.avatar.vendor` (string) - The avatar provider. `generic` passes provider-neutral parameters through.
      - Allowed: `akool` | `liveavatar` | `anam` | `sensetime` | `spatius` | `generic`
    - `pipeline.avatar.params` (object) - Provider-specific settings, passed to the provider as they are.
- `filler_words` (object) - Short phrases the agent says while it waits for the LLM to respond, such as when it waits for a tool call.
  - `filler_words.enable` (boolean) - Whether to play filler words.
    - Default: `false`
  - `filler_words.trigger` (object) - When to play a filler word.
    - `filler_words.trigger.mode` (string, required) - `fixed_time`: Play a filler word when the LLM hasn't responded within `response_wait_ms`.
    - `filler_words.trigger.response_wait_ms` (integer, required) - How long in milliseconds to wait for the LLM response before playing a filler word, for example `1500`.
      - Range: `[100, 10000]`
  - `filler_words.content` (object) - Where the filler words come from. Pass the fields of one source only.
    - `filler_words.content.mode` (string, required) - `static`: Pick a phrase from `phrases`.
    - `filler_words.content.instructions` (string, required) - The instructions used to generate filler phrases. They can't be empty or only whitespace.
      - Min length: `1`
    - `filler_words.content.fallback_strategy` (string) - What to use when a generated phrase isn't available.
      - Default: `static`
    - `filler_words.content.phrases` (array, required) - The filler phrases, 1 to 100 non-empty strings.
      - Min items: `1`
      - Max items: `100`
      - `filler_words.content.phrases.items` (string)
        - Min length: `1`
    - `filler_words.content.selection_rule` (string) - How the agent picks the next phrase:

- `shuffle`: In random order.
- `round_robin`: In list order, starting over after the last phrase.
      - Allowed: `shuffle` | `round_robin`
      - Default: `shuffle`

### Request body example

```json
{
  "agent_name": "Customer Support Agent",
  "instructions": "You are a support agent for Acme. Address the caller as {{customer_name}}. Be concise.",
  "variables": {
    "customer_name": {
      "type": "str",
      "required": true
    },
    "resolution_status": {
      "type": "str",
      "options": [
        "resolved",
        "follow_up",
        "unresolved"
      ],
      "extract": true,
      "description": "Outcome of the conversation."
    }
  },
  "pipeline": {
    "mode": "cascaded",
    "asr": {
      "credential": {
        "mode": "managed"
      },
      "vendor": "deepgram",
      "model": "nova-3",
      "language": "en-US"
    },
    "llm": {
      "credential": {
        "mode": "byok",
        "fields": {
          "api_key": "$secrets.support_prod.llm"
        }
      },
      "vendor": "openai",
      "style": "openai",
      "model": "<model_name>",
      "max_history": 12,
      "failure_message": "I'm sorry, I couldn't generate a response. Please try again."
    },
    "tts": {
      "credential": {
        "mode": "managed"
      },
      "vendor": "minimax",
      "params": {
        "model": "speech-2.6-turbo",
        "voice_setting": {
          "voice_id": "English_captivating_female1",
          "speed": 1
        }
      }
    },
    "silence_config": {
      "enabled": true,
      "timeout_ms": 3000,
      "action": "speak",
      "content": "Hello, how can I help you?"
    }
  },
  "greeting": {
    "mode": "text",
    "on": "each_join",
    "delay_ms": 0,
    "text": "Hi, thanks for calling Acme."
  }
}
```

## Responses

### 201

The agent was created. The response body contains the saved agent.

- `agent_id` (string, required) - The ID of the agent, a UUID.
  - Format: `uuid`
- `agent_name` (string, required) - A name for the agent, 1 to 255 characters. It doesn't need to be unique.
  - Min length: `1`
  - Max length: `255`
- `instructions` (string) - The system instructions that define how the agent behaves. The model receives them as the first `system` message, before `messages`.
- `messages` (array) - Messages that start the conversation history. They follow `instructions`.
  - `messages.items` (object)
    - `messages.items.role` (string, required) - The author of the message:

- `system`: An instruction to the model.
- `user`: A message from the user.
- `assistant`: A message from the agent.
      - Allowed: `system` | `user` | `assistant`
    - `messages.items.content` (string, required) - The text of the message.
- `variables` (object) - Every variable the agent uses, keyed by variable name. A name starts with a lowercase letter and contains up to 64 lowercase letters, digits, and underscores. Reference a variable as `{{variable_name}}` in `instructions`, `messages`, `greeting.text`, `pipeline.llm.failure_message`, `pipeline.silence_config.content`, and generated filler instructions. The agent doesn't store values: pass them in `values` when you start a session, or in each campaign contact. Expansion is single-pass, so a value that contains a placeholder isn't expanded again.
  - Example: `{"customer_name":{"type":"str","required":true},"resolution_status":{"type":"str","options":["resolved","follow_up","unresolved"],"extract":true,"description":"Outcome of the conversation."}}`
- `greeting` (object) - The message the agent speaks when a participant joins. If you omit it, the agent doesn't greet participants automatically.
  - `greeting.mode` (string, required) - `text`: The agent speaks `text` using the TTS stage.
  - `greeting.on` (string) - Which participants the agent greets:

- `each_join`: Every participant who joins.
- `first_join`: Only the first participant to join.
    - Allowed: `each_join` | `first_join`
    - Default: `each_join`
  - `greeting.delay_ms` (integer) - The delay in milliseconds between a participant joining and the agent starting the greeting.
    - Default: `0`
    - Range: `[0, 5000]`
  - `greeting.text` (string, required) - The greeting text.
- `pipeline` (object) - The models that power the agent: separate ASR, LLM, and TTS stages. When you pass a pipeline, `llm` and `tts` are required.
  - `pipeline.mode` (string, required) - `cascaded`: Separate ASR, LLM, and TTS stages.
  - `pipeline.asr` (object) - The automatic speech recognition (ASR) stage, which transcribes the user's speech.
    - `pipeline.asr.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.asr.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.asr.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.asr.vendor` (string) - The ASR provider, for example `deepgram`.
    - `pipeline.asr.model` (string) - The provider model, for example `nova-3`.
    - `pipeline.asr.language` (string) - The language of the user's speech, for example `en-US`.
    - `pipeline.asr.url` (string) - A custom provider endpoint. Don't also put the endpoint in `params`.
    - `pipeline.asr.params` (object) - Provider-specific settings that aren't secret, such as `keywords`, passed to the provider as they are. Keep API keys in `credential`, not here.
  - `pipeline.llm` (object, required) - The large language model (LLM) stage, which generates the agent's replies.
    - `pipeline.llm.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.llm.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.llm.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.llm.vendor` (string) - The LLM provider, for example `openai`.
    - `pipeline.llm.style` (string) - The API style used to communicate with the model, for example `openai` for OpenAI and OpenAI-compatible APIs.
    - `pipeline.llm.model` (string) - The provider model.
    - `pipeline.llm.url` (string) - A custom provider endpoint. Don't also put the endpoint in `params`. For Google Gemini in an ephemeral session, the URL can include your API key as `key=<your_api_key>`, with `credential.fields` left out. A saved agent's `url` can't contain a key.
    - `pipeline.llm.headers` (object) - Custom HTTP headers sent to the provider endpoint, at most 32. They are write-only and are never returned. A value can be a whole `$secrets.<set>.<key>` reference when the stage uses `byok` and an approved provider endpoint. Agora resolves it when each session starts. A header name that looks like a credential, such as `X-Api-Key`, must use a reference. Transport, identity, and provider authentication headers are reserved. Set provider authentication in `credential`.
    - `pipeline.llm.params` (object) - Provider-specific settings that aren't secret, such as `reasoning_effort`, passed to the provider as they are. Keep API keys in `credential`, not here.
    - `pipeline.llm.input_modalities` (array) - The input types sent to the model, for example `["text"]`.
      - `pipeline.llm.input_modalities.items` (string)
    - `pipeline.llm.output_modalities` (array) - The output types requested from the model, for example `["text"]`.
      - `pipeline.llm.output_modalities.items` (string)
    - `pipeline.llm.max_history` (integer) - The number of recent conversation messages the LLM keeps as context. It counts messages, not user and agent pairs. The agent's `instructions` don't count toward this limit.
      - Default: `32`
      - Range: `[1, 1024]`
    - `pipeline.llm.failure_message` (string) - The message the agent speaks with the TTS stage when the LLM fails to produce a response.
    - `pipeline.llm.tools` (array) - Function tools the model can call. An agent can have up to 32 tools in total, including tools discovered from `mcp_servers`. Tool names must be unique, ignoring case.
      - Max items: `32`
      - `pipeline.llm.tools.items` (object)
        - `pipeline.llm.tools.items.type` (string, required) - Always `function`.
        - `pipeline.llm.tools.items.name` (string, required) - The tool name shown to the model. It starts with a letter and contains only letters and digits, up to 64 characters. It can't start with `mcp`, can't be `ragsearch_tool`, and can't match an MCP, built-in, or model-native tool.
          - Max length: `64`
        - `pipeline.llm.tools.items.description` (string, required) - What the tool does, 1 to 1,024 characters. The model uses it to decide when to call the tool.
          - Min length: `1`
          - Max length: `1024`
        - `pipeline.llm.tools.items.parameters` (object, required) - A JSON Schema that describes the tool's arguments.
        - `pipeline.llm.tools.items.execution` (object, required) - How the agent runs the tool.
          - `pipeline.llm.tools.items.execution.mode` (string, required) - `sync`: The agent waits for the tool response before it continues.
        - `pipeline.llm.tools.items.server` (object, required) - The HTTP endpoint the agent calls to run the tool.
          - `pipeline.llm.tools.items.server.method` (string, required) - The HTTP method.
            - Allowed: `GET` | `POST`
          - `pipeline.llm.tools.items.server.url` (string, required) - The HTTPS URL of the endpoint, up to 2,048 characters.
            - Min length: `1`
            - Max length: `2048`
            - Pattern: `^https://`
          - `pipeline.llm.tools.items.server.headers` (object) - HTTP headers sent to the endpoint, up to 32. They are write-only and are never returned.
          - `pipeline.llm.tools.items.server.body` (object) - Additional fields to include in the request body.
          - `pipeline.llm.tools.items.server.timeout_ms` (integer) - The request timeout in milliseconds.
            - Default: `10000`
            - Range: `[1000, 100000]`
    - `pipeline.llm.mcp_servers` (array) - MCP servers whose tools the model can call.
      - `pipeline.llm.mcp_servers.items` (object)
        - `pipeline.llm.mcp_servers.items.name` (string, required) - A name for the MCP server, 1 to 48 letters and digits.
          - Min length: `1`
          - Max length: `48`
        - `pipeline.llm.mcp_servers.items.endpoint` (string, required) - The HTTPS URL of the MCP server, up to 2,048 characters.
          - Min length: `1`
          - Max length: `2048`
          - Pattern: `^https://`
        - `pipeline.llm.mcp_servers.items.transport` (string, required) - The MCP transport. `streamable_http`: Streamable HTTP.
        - `pipeline.llm.mcp_servers.items.headers` (object) - HTTP headers sent to the MCP server, up to 32. They are write-only and are never returned.
        - `pipeline.llm.mcp_servers.items.allowed_tools` (array) - The tools the model can call from this server:

- Omit the field, or include `*` in the list, to allow every tool.
- Pass an empty list to allow no tools.
- Otherwise, only the listed tools are allowed.
          - `pipeline.llm.mcp_servers.items.allowed_tools.items` (string)
            - Min length: `1`
        - `pipeline.llm.mcp_servers.items.timeout_ms` (integer) - The request timeout in milliseconds.
          - Range: `[1000, 100000]`
  - `pipeline.tts` (object, required) - The text-to-speech (TTS) stage, which converts the agent's replies to speech.
    - `pipeline.tts.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.tts.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.tts.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.tts.vendor` (string) - The TTS provider, for example `microsoft`.
    - `pipeline.tts.model` (string) - The TTS model the agent uses. Returned in responses only.
    - `pipeline.tts.url` (string) - A custom provider endpoint. Don't also put the endpoint in `params`.
    - `pipeline.tts.headers` (object) - Custom HTTP headers sent to the provider endpoint, at most 32. They are write-only and are never returned. A value can be a whole `$secrets.<set>.<key>` reference when the stage uses `byok` and an approved provider endpoint. Agora resolves it when each session starts. A header name that looks like a credential, such as `X-Api-Key`, must use a reference. Transport, identity, and provider authentication headers are reserved. Set provider authentication in `credential`.
    - `pipeline.tts.params` (object) - Provider-specific settings that aren't secret, such as the voice and, where the provider requires it, the model, passed to the provider as they are. Keep API keys in `credential`, not here.
    - `pipeline.tts.skip_patterns` (array) - Bracketed text that the agent doesn't speak, such as tone or action notes in the LLM
output:

- `fullwidth_parentheses`: Text in `（）`.
- `lenticular_brackets`: Text in `【】`.
- `parentheses`: Text in `( )`.
- `square_brackets`: Text in `[ ]`.
- `curly_braces`: Text in `{ }`.
      - `pipeline.tts.skip_patterns.items` (string)
        - Allowed: `fullwidth_parentheses` | `lenticular_brackets` | `parentheses` | `square_brackets` | `curly_braces`
  - `pipeline.turn_detection` (object) - How the agent detects when the user starts and stops speaking, and handles interruptions.
    - `pipeline.turn_detection.speech_threshold` (number) - The speech detection threshold. Lower values detect speech more easily. Higher values ignore weak speech.
      - Default: `0.5`
      - Range: `[0, 1]`
    - `pipeline.turn_detection.start_of_speech` (object) - How the agent detects that the user starts speaking. Pass the fields of one detection type only. If you omit it, the agent uses `semantic` detection with its defaults.
      - `pipeline.turn_detection.start_of_speech.type` (string, required) - `semantic`: Detects a meaningful start of speech. If you omit `type`, the agent uses `semantic`.
        - Default: `semantic`
      - `pipeline.turn_detection.start_of_speech.interrupt_duration_ms` (integer) - Speech shorter than this, in milliseconds, is ignored.
        - Default: `160`
        - Range: `[120, 1200]`
      - `pipeline.turn_detection.start_of_speech.speaking_interrupt_duration_ms` (integer) - How long in milliseconds the user must speak to interrupt the agent while it's speaking.
        - Default: `160`
      - `pipeline.turn_detection.start_of_speech.prefix_padding_ms` (integer) - How many milliseconds of audio before the detected start of speech to keep, so the beginning of the speech isn't clipped.
        - Default: `800`
        - Range: `[0, 5000]`
    - `pipeline.turn_detection.end_of_speech` (object) - How the agent detects that the user has finished speaking. Pass the fields of one detection type only. If you omit it, the agent uses `semantic` detection with its defaults.
      - `pipeline.turn_detection.end_of_speech.type` (string, required) - `semantic`: Considers whether the utterance sounds complete. If you omit `type`, the agent uses `semantic`.
        - Default: `semantic`
      - `pipeline.turn_detection.end_of_speech.silence_duration_ms` (integer) - The silence in milliseconds that ends the turn.
        - Default: `320`
        - Range: `[120, 2000]`
      - `pipeline.turn_detection.end_of_speech.max_wait_ms` (integer) - The maximum time in milliseconds to wait before ending the turn, from 500 to 10,000. Set to `-1` to wait without a timeout.
        - Default: `3000`
        - Range: `[500, 10000]`
      - `pipeline.turn_detection.end_of_speech.pause_state_enabled` (boolean) - Whether the agent detects a pause within an utterance, such as "hold on", and waits for more input.
        - Default: `true`
    - `pipeline.turn_detection.interruption` (object) - How the user can interrupt the agent while it speaks or thinks. Pass the fields of one interruption mode only. If you omit it, any user speech interrupts the agent.
      - Default: `{"mode":"start_of_speech"}`
      - `pipeline.turn_detection.interruption.mode` (string, required) - `start_of_speech`: Any user speech interrupts the agent.
      - `pipeline.turn_detection.interruption.when_off` (string) - What happens to user speech that arrives while the agent speaks or thinks:

- `append`: The agent responds to the speech after the current reply.
- `ignore`: The agent discards the speech.
        - Allowed: `append` | `ignore`
        - Default: `append`
      - `pipeline.turn_detection.interruption.keywords` (array, required) - The phrases that interrupt the agent, 1 to 128.
        - Min items: `1`
        - Max items: `128`
        - `pipeline.turn_detection.interruption.keywords.items` (string)
          - Min length: `1`
  - `pipeline.silence_config` (object) - What the agent does after a period of silence. This only prompts the user. To hang up a phone call after silence, use `call_policy.max_silence_duration_ms`.
    - `pipeline.silence_config.enabled` (boolean, required) - Whether to act on silence.
    - `pipeline.silence_config.timeout_ms` (integer, required) - How long the silence lasts, in milliseconds, before the agent acts, up to 60,000. Applies only when `enabled` is `true`.
      - Range: `[0, 60000]`
    - `pipeline.silence_config.action` (string, required) - What the agent does when the timeout is reached:

- `speak`: Speaks `content` as written.
- `think`: Gives `content` to the LLM as an instruction, and responds to it.
      - Allowed: `speak` | `think`
    - `pipeline.silence_config.content` (string, required) - The text to speak, or the instruction for the LLM, depending on `action`.
  - `pipeline.avatar` (object) - The avatar that represents the agent on video.
    - `pipeline.avatar.enabled` (boolean) - Whether to show the avatar.
      - Default: `false`
    - `pipeline.avatar.credential` (object) - How this stage authenticates with the provider. Set it explicitly on each stage. Pass the fields of one credential mode only. Responses return only `mode`.
      - `pipeline.avatar.credential.mode` (string, required) - `managed`: Agora provides the provider authentication.
      - `pipeline.avatar.credential.fields` (object) - Your provider credentials, keyed by the provider's field name. Most providers take one field, `api_key`. Some use other names, such as `key` or `credentials`, or take more than one field, such as an AWS access key ID and secret access key. Each model page lists its provider's fields. In an agent, each value must be a `$secrets.<set_name>.<key>` reference to a [secret set](/en/api-reference/api-ref/conversational-ai/platform/create-secret-set). An ephemeral session also accepts the values themselves. Agora resolves references when each session starts. The values are never returned.
    - `pipeline.avatar.vendor` (string) - The avatar provider. `generic` passes provider-neutral parameters through.
      - Allowed: `akool` | `liveavatar` | `anam` | `sensetime` | `spatius` | `generic`
    - `pipeline.avatar.params` (object) - Provider-specific settings, passed to the provider as they are.
- `filler_words` (object) - Short phrases the agent says while it waits for the LLM to respond, such as when it waits for a tool call.
  - `filler_words.enable` (boolean) - Whether to play filler words.
    - Default: `false`
  - `filler_words.trigger` (object) - When to play a filler word.
    - `filler_words.trigger.mode` (string, required) - `fixed_time`: Play a filler word when the LLM hasn't responded within `response_wait_ms`.
    - `filler_words.trigger.response_wait_ms` (integer, required) - How long in milliseconds to wait for the LLM response before playing a filler word, for example `1500`.
      - Range: `[100, 10000]`
  - `filler_words.content` (object) - Where the filler words come from. Pass the fields of one source only.
    - `filler_words.content.mode` (string, required) - `static`: Pick a phrase from `phrases`.
    - `filler_words.content.instructions` (string, required) - The instructions used to generate filler phrases. They can't be empty or only whitespace.
      - Min length: `1`
    - `filler_words.content.fallback_strategy` (string) - What to use when a generated phrase isn't available.
      - Default: `static`
    - `filler_words.content.phrases` (array, required) - The filler phrases, 1 to 100 non-empty strings.
      - Min items: `1`
      - Max items: `100`
      - `filler_words.content.phrases.items` (string)
        - Min length: `1`
    - `filler_words.content.selection_rule` (string) - How the agent picks the next phrase:

- `shuffle`: In random order.
- `round_robin`: In list order, starting over after the last phrase.
      - Allowed: `shuffle` | `round_robin`
      - Default: `shuffle`
- `create_ts` (integer, required) - The time the agent was created, as a Unix timestamp in seconds.
- `update_ts` (integer, required) - The time the agent was last updated, as a Unix timestamp in seconds.
### 400

The request is invalid. For example, `InvalidFieldValue` when a pipeline stage has an unsupported vendor, model, or credential mode, `MissingRequiredField` when a stage is missing a provider parameter or its BYOK `fields`, `UnknownSecretReference` when a `$secrets` reference names a set or key that doesn't exist, `InvalidModuleParameter` when the resulting pipeline is invalid, or `NotImplemented` when a field isn't available yet.

- `detail` (string, required) - A human-readable description of the error. It names the field or resource involved, when there is one. Don't parse it; branch on `reason` instead.
- `reason` (string, required) - A machine-readable error reason, such as `InvalidFieldValue` or `ResourceNotFound`. For all reasons, see [Status codes](/en/api-reference/api-ref/conversational-ai/platform/status-codes).
### 401

Authentication is missing or invalid. The reason is `InvalidPermission`.

- `detail` (string, required) - A human-readable description of the error. It names the field or resource involved, when there is one. Don't parse it; branch on `reason` instead.
- `reason` (string, required) - A machine-readable error reason, such as `InvalidFieldValue` or `ResourceNotFound`. For all reasons, see [Status codes](/en/api-reference/api-ref/conversational-ai/platform/status-codes).
### default

The request failed. The response body contains the `reason` and `detail` of the failure. For possible reasons, see [Status codes](/en/api-reference/api-ref/conversational-ai/platform/status-codes).

- `detail` (string, required) - A human-readable description of the error. It names the field or resource involved, when there is one. Don't parse it; branch on `reason` instead.
- `reason` (string, required) - A machine-readable error reason, such as `InvalidFieldValue` or `ResourceNotFound`. For all reasons, see [Status codes](/en/api-reference/api-ref/conversational-ai/platform/status-codes).

## Response examples

### 201

```json
{
  "agent_id": "3f6c2a9e-8d1b-4e7a-9c55-0b2f7d1e4a10",
  "agent_name": "Customer Support Agent",
  "instructions": "You are a support agent for Acme. Address the caller as {{customer_name}}. Be concise.",
  "variables": {
    "customer_name": {
      "type": "str",
      "required": true
    },
    "resolution_status": {
      "type": "str",
      "options": [
        "resolved",
        "follow_up",
        "unresolved"
      ],
      "extract": true,
      "description": "Outcome of the conversation."
    }
  },
  "pipeline": {
    "mode": "cascaded",
    "asr": {
      "credential": {
        "mode": "managed"
      },
      "vendor": "deepgram",
      "model": "nova-3",
      "language": "en-US"
    },
    "llm": {
      "credential": {
        "mode": "byok"
      },
      "vendor": "openai",
      "style": "openai",
      "model": "<model_name>",
      "max_history": 12,
      "failure_message": "I'm sorry, I couldn't generate a response. Please try again."
    },
    "tts": {
      "credential": {
        "mode": "managed"
      },
      "vendor": "minimax",
      "params": {
        "model": "speech-2.6-turbo",
        "voice_setting": {
          "voice_id": "English_captivating_female1",
          "speed": 1
        }
      }
    },
    "silence_config": {
      "enabled": true,
      "timeout_ms": 3000,
      "action": "speak",
      "content": "Hello, how can I help you?"
    }
  },
  "greeting": {
    "mode": "text",
    "on": "each_join",
    "delay_ms": 0,
    "text": "Hi, thanks for calling Acme."
  },
  "create_ts": 1786543331,
  "update_ts": 1786543331
}
```
