---
title: "Create Agent"
method: POST
path: "/v1/agent/agents"
tags: ["Agents"]
---

# Create Agent

`POST /v1/agent/agents`

## Request

- Base URL: `https://api.fish.audio`
- URL: `https://api.fish.audio/v1/agent/agents`
- Auth: HTTP bearer

Create an agent, optionally with its full initial configuration inline —
one call provisions a ready-to-publish agent. The agent starts as a draft:
publish it before creating sessions with it.

## Request body

- PublicAgentCreatePayload
  - `name` string, required
  - `description` string
  - `config` PublicAgentConfigPatchPayload
    - `prompt` PublicAgentPromptPatch
      - `system_prompt` string, nullable: Limited to 32000 tokens (o200k_base); keeping it under 2000 tokens is recommended for latency and cost.
      - `first_message_mode` 'off' | 'fixed' | 'prompt', nullable
      - `first_message` string, nullable
      - `first_message_prompt` string, nullable
    - `voice` PublicAgentVoicePatch
      - `voice_id` string, nullable
      - `speaking_language` 'en' | 'ja' | 'zh' | 'ko' | 'es' | 'fr' | 'de' | 'pt' | 'it' | 'nl' | 'ar' | 'hi' | 'id' | 'tr' | 'ru' | 'bg' | 'sr' | 'hr' | 'cs' | 'sk' | 'pl' | 'uk' | 'ro' | 'hu' | 'el' | 'sv' | 'da' | 'no' | 'fi' | 'et' | 'lv' | 'lt' | 'ca' | 'he' | 'fa' | 'ur' | 'kk' | 'ka' | 'hy' | 'bn' | 'ta' | 'te' | 'kn' | 'mr' | 'gu' | 'pa' | 'ne' | 'th' | 'vi' | 'ms' | 'tl' | 'af', nullable
      - `expressive` boolean, nullable
      - `speed` number, nullable
      - `tts_model` 's2.1-pro' | 'drama-3', nullable
      - `pronunciation_dictionary_ids` string[], nullable
    - `asr` PublicAgentAsrPatch
      - `model` 'deepgram:nova-3' | 'elevenlabs:scribe_v2_realtime' | 'elevenlabs:scribe_v2_medical' | 'fish_audio:transcribe-stream-1', nullable
      - `multilingual` boolean, nullable
      - `strict_language` boolean, nullable: Enforces voice.speaking_language whatever multilingual says: speech recognized as another language reaches the agent as [unintelligible speech]. Language is detected per utterance, so a short or heavily accented phrase in the speaking language can occasionally be detected as another language and replaced too. With deepgram:nova-3, speech in another language is usually not transcribed at all. Only enable it when you explicitly need to stop the agent from understanding other languages.
      - `keyterms` string[], nullable
    - `conversation` PublicAgentConversationPatch
      - `max_duration_seconds` integer, nullable
      - `response_wait_ms` integer, nullable
      - `eagerness` 'relaxed' | 'balanced' | 'eager', nullable
      - `response_max_wait_ms` integer, nullable
      - `interruptible` boolean, nullable
      - `interruption_sensitivity` 'low' | 'balanced' | 'high', nullable
      - `interruption_ignore_phrases` string[], nullable
      - `speculative_response` boolean, nullable
      - `reengage_enabled` boolean, nullable
      - `hangup_after_seconds` integer, nullable
      - `hangup_farewell_mode` 'generated' | 'fixed' | 'off', nullable: What the agent says before a silence hangup. `off` (default) hangs up without a word, `generated` says a short goodbye, `fixed` says hangup_farewell_message. A `fixed` mode with an empty message falls back to `generated`. If the caller speaks during the goodbye, the call goes on.
      - `hangup_farewell_message` string, nullable: The goodbye said verbatim in the `fixed` hangup_farewell_mode, at most 500 characters. `{{variable}}` placeholders are filled in for each session. "" clears it.
      - `record_audio` boolean, nullable
      - `timezone` string, nullable
      - `transfer_destinations` AgentTransferDestinationPatch[], nullable
        - `type` 'phone' | 'sip'
        - `label` string
        - `phone_number` string, nullable
        - `sip_uri` string, nullable
        - `headers` object
        - `description` string
        - `mode` 'cold' | 'warm'
        - `warm_connect` 'confirm' | 'briefing' | 'direct'
        - `warm_briefing_instructions` string
        - `warm_ring_timeout_seconds` integer
        - `warm_confirm_timeout_seconds` integer
        - `warm_human_detection` boolean: Applies to warm transfers whose warm_connect is `confirm` or `briefing`, and is ignored for cold transfers and `direct`. When true, once the destination answers, the agent stays silent until the other side speaks, then classifies the pickup. A person is briefed as usual. Voicemail or an IVR (phone menu) ends the transfer as failed. A hold queue or silence keeps the agent waiting until `warm_confirm_timeout_seconds` ends the attempt as unreachable. A failed transfer follows `on_failure`. Leave it off for destinations whose people are only reachable through a phone menu, because the menu stops the transfer.
        - `warm_consult_uses_agent_settings` boolean: Compatibility option for warm transfers whose warm_connect is `confirm` or `briefing`, ignored for cold transfers and `direct`. Absent or true (recommended): on the call in which the agent briefs the destination, the agent uses the same turn-taking (response timing: `eagerness`, `response_wait_ms` and `response_max_wait_ms`), interruption (`interruption_sensitivity` and `interruption_ignore_phrases`) and language (`voice.speaking_language`) settings as on the call with the caller. False: that call keeps the older fixed behavior. Destinations that existed before this setting were set to false so their behavior did not change. Either way the destination can always interrupt the agent on that call, whatever `interruptible` is set to. A destination sent without the field keeps the value of the existing destination it replaces (same label, else a renamed one with the same phone number or SIP URI) and is true when it is new. Send true to switch a destination to the agent's settings.
        - `on_failure` AgentTransferOnFailurePatch
          - `action` 'return_to_agent' | 'end_call'
          - `message` string
        - `announcement_mode` 'generated' | 'fixed' | 'off'
        - `announcement_message` string
      - `transfer_refuse_questions` boolean, nullable
      - `outbound` AgentOutboundPatch
        - `amd_enabled` boolean
        - `voicemail` AgentOutboundVoicemailPatch
          - `action` 'hangup' | 'leave_message' | 'leave_prompt_message'
          - `message` string
      - `keypad_menu` AgentKeypadMenuPatch
        - `enabled` boolean
        - `prompt` string
        - `options` AgentKeypadMenuOptionPatch[]
          - `key` string, required
          - `value` string, required
        - `accept_speech` boolean
        - `variable` string
        - `no_input_timeout_seconds` integer
        - `max_attempts` integer
        - `retry_message` string
        - `fallback_value` string
      - `keypad_input` AgentKeypadInputPatch
        - `enabled` boolean
        - `terminator` '#' | '*' | 'none'
        - `inter_digit_timeout_seconds` integer
      - `echo_guard` AgentEchoGuardPatch
        - `web_voice` boolean, nullable: Voice calls in a browser or app (web widget, SDK, test calls). Null uses the default (on).
        - `phone_inbound` boolean, nullable: Inbound phone calls. Null uses the default (on).
        - `phone_outbound` boolean, nullable: Outbound phone calls. Null uses the default (off).
    - `tools` PublicAgentToolsPatch
      - `enabled` boolean, nullable
      - `tool_ids` string[], nullable: Replaces the attached tool set wholesale. Every id must be an existing tool in the agent's workspace, else 422.
      - `system_tools` PublicSystemToolsPatch
        - `hang_up_call` boolean, nullable
        - `hang_up_call_farewell_mode` 'generated' | 'fixed' | 'off', nullable
        - `hang_up_call_farewell_message` string, nullable
        - `hang_up_call_refuse_questions` boolean, nullable
    - `webhooks` PublicAgentWebhooksPatch
      - `conversation_init` PublicConversationInitWebhookPayload
        - `url` string, required
        - `secret` string, nullable: Optional HMAC-SHA256 signing secret (X-Fish-Webhook-Signature). Write-only: reads report `has_secret` instead of the value. Omit it (or send null) to keep the secret already stored for the same url; send an empty string to remove it.
        - `timeout_seconds` number: How long the platform waits for the endpoint before connecting the call without variables.
      - `post_call` PublicPostCallWebhookPayload[], nullable: Up to 5 endpoints, each receiving every post-call event. Replaces the configured list wholesale; a single object is still accepted and is stored as a one-element list, and null (or an empty list) clears every endpoint. URLs must be unique.
        - `url` string, required
        - `secret` string, nullable: Optional HMAC-SHA256 signing secret (X-Fish-Webhook-Signature). Write-only: reads report `has_secret` instead of the value. Omit it (or send null) to keep the secret already stored for the same url; send an empty string to remove it.
    - `knowledge_base` PublicAgentKnowledgeBasePatch
      - `enabled` boolean, nullable: Whether the agent retrieves from its attached sources. Defaults to true when omitted alongside a non-empty knowledge_source_ids; send false explicitly to attach sources without using them yet.
      - `knowledge_source_ids` string[], nullable: Replaces the attached knowledge sources wholesale. Every id must be an existing knowledge source visible to the agent, else 422.
    - `analysis` PublicAgentAnalysisPatch
      - `summary` PublicAgentAnalysisSummaryPatch
        - `enabled` boolean
        - `prompt` string, nullable
        - `language` 'en' | 'ja' | 'zh' | 'ko' | 'es' | 'fr' | 'de' | 'pt' | 'it' | 'nl' | 'ar' | 'hi' | 'id' | 'tr' | 'ru' | 'bg' | 'sr' | 'hr' | 'cs' | 'sk' | 'pl' | 'uk' | 'ro' | 'hu' | 'el' | 'sv' | 'da' | 'no' | 'fi' | 'et' | 'lv' | 'lt' | 'ca' | 'he' | 'fa' | 'ur' | 'kk' | 'ka' | 'hy' | 'bn' | 'ta' | 'te' | 'kn' | 'mr' | 'gu' | 'pa' | 'ne' | 'th' | 'vi' | 'ms' | 'tl' | 'af'
      - `data_fields` PublicAgentAnalysisDataField[], nullable
        - `name` string, required
        - `type` 'boolean' | 'text' | 'number' | 'enum'
        - `description` string
        - `enum_options` string[], nullable
      - `criteria` PublicAgentAnalysisCriterion[], nullable
        - `name` string, required
        - `description` string
    - `guardrails` PublicAgentGuardrailsPatch
      - `focus` boolean, nullable
      - `manipulation` boolean, nullable
      - `pre_tool_speech` boolean, nullable
    - `context` PublicAgentContextPatch
      - `current_time` boolean, nullable
    - `llm` PublicAgentLLMPatch
      - `model` 'google/gemini-3.5-flash-lite' | 'google/gemini-3.6-flash' | 'google/gemini-3.8-flash' | 'google/gemini-3.6-flash-priority' | 'google/gemini-3.8-flash-priority' | 'google/gemini-2.5-flash' | 'google/gemma-4-26b-a4b-it' | 'anthropic/claude-haiku-4.5' | 'anthropic/claude-sonnet-4.6' | 'anthropic/claude-opus-5.5' | 'anthropic/claude-fable-5.1' | 'openai/gpt-5.6-luna' | 'openai/gpt-6-luna' | 'openai/gpt-5' | 'openai/gpt-6.1-sol' | 'openai/gpt-6-astra' | 'openai/gpt-4o' | 'openai/gpt-4.1', nullable
      - `custom` PublicAgentLLMCustomConfig
        - `base_url` string, required
        - `model` string, required
        - `api_key` string, nullable: Sent to your endpoint as the Authorization bearer token; write-only, reads return null. Required when introducing `custom`; omit to keep the stored key when updating.
        - `headers` object, nullable: Extra headers sent on every request to your endpoint, such as `x-vercel-protection-bypass`. Reads return the stored map. Omit to keep the stored headers, `{}` clears them.
      - `fallback` PublicAgentLLMFallback
        - `mode` 'platform' | 'manual' | 'off', required
        - `models` PublicAgentLLMFallbackModel[], nullable: Fallback models in order, 1 to 3. Only with mode `manual`.
          - `model` 'google/gemini-3.5-flash-lite' | 'google/gemini-3.6-flash' | 'google/gemini-3.8-flash' | 'google/gemini-3.6-flash-priority' | 'google/gemini-3.8-flash-priority' | 'google/gemini-2.5-flash' | 'google/gemma-4-26b-a4b-it' | 'anthropic/claude-haiku-4.5' | 'anthropic/claude-sonnet-4.6' | 'anthropic/claude-opus-5.5' | 'anthropic/claude-fable-5.1' | 'openai/gpt-5.6-luna' | 'openai/gpt-6-luna' | 'openai/gpt-5' | 'openai/gpt-6.1-sol' | 'openai/gpt-6-astra' | 'openai/gpt-4o' | 'openai/gpt-4.1', nullable
          - `custom` PublicAgentLLMCustomConfig
            - `base_url` string, required
            - `model` string, required
            - `api_key` string, nullable: Sent to your endpoint as the Authorization bearer token; write-only, reads return null. Required when introducing `custom`; omit to keep the stored key when updating.
            - `headers` object, nullable: Extra headers sent on every request to your endpoint, such as `x-vercel-protection-bypass`. Reads return the stored map. Omit to keep the stored headers, `{}` clears them.
          - `ttft_timeout_ms` integer, nullable: Milliseconds the model gets to start its reply (first text or tool call). Past it, the reply moves to the next fallback model. Not set: defaults to 5000 ms (5 seconds).
          - `total_timeout_ms` integer, nullable: Milliseconds the model gets to finish its reply. Past it, a reply nothing of which was spoken yet moves to the next fallback model; one already being spoken fails like any failed reply. At least `ttft_timeout_ms` when both are set. Not set: no limit.
          - `parallel` AgentLLMParallelPayload
            - `kick_in_ms` integer: The agent's own model keeps the reply if its first token comes within this many milliseconds; past it, this fallback's reply is used. The other request is cancelled.
        - `primary` AgentLLMTimeoutsPayload: The agent's own model's timeouts, write model. Unset ttft_timeout_ms = 5000 ms (5 seconds); unset total_timeout_ms = no limit. The two are compared only when both are set.
          - `ttft_timeout_ms` integer, nullable: Milliseconds the model gets to start its reply (first text or tool call). Past it, the reply moves to the next fallback model. Not set: defaults to 5000 ms (5 seconds).
          - `total_timeout_ms` integer, nullable: Milliseconds the model gets to finish its reply. Past it, a reply nothing of which was spoken yet moves to the next fallback model; one already being spoken fails like any failed reply. At least `ttft_timeout_ms` when both are set. Not set: no limit.
    - `variables` PublicAgentVariablesPatch
      - `defaults` object, nullable
      - `on_missing` 'keep' | 'empty' | 'reject', nullable

## Response `201`

Document created, URL follows

- object
  - `agent_id` string, required
  - `workspace_id` string, required
  - `name` string, required
  - `description` string
  - `status` 'active' | 'archived', required
  - `publication_state` 'live' | 'draft', required: `live` once at least one version is published; `draft` agents cannot take sessions yet.
  - `public_enabled` boolean
  - `allowed_origins` string[]
  - `overrides_allowed` string[]
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `401`: No permission -- see authorization schemes
- `403`: Request forbidden -- authorization will not help
- `422`
- `503`: The server cannot process the request due to a high load

## Changes

- **2026-11-03** `42ac421f7a12`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/fish/apis/fishaudio-openapi/changes/v1/agent/agents/post.md)

---

[API](https://skmtc.dev/fish/apis/fishaudio-openapi.md) - [All operations](https://skmtc.dev/fish/apis/fishaudio-openapi/llms.txt) - [OpenAPI document](https://skmtc.dev/fish/apis/fishaudio-openapi/revisions/42ac421f7a12?raw)
