---
title: "Create an external or hosted agent"
method: POST
path: "/v1/agents"
---

# Create an external or hosted agent

`POST /v1/agents`

## Request

- Base URL: `https://api.skysay.ai`
- URL: `https://api.skysay.ai/v1/agents`
- Auth: none declared

## Request body

- Agent
  - `audio_stream_url` string
  - `begin_message` string
  - `follow_caller` AgentFollowCaller: Explicit saved and published automatic multilingual policy. Existing clients may send only enabled; omitted scope means selected. With scope all, the server resolves every language compatible with the exact stack at publication and freezes that concrete set. With selected, languages supplies the allowed subset. Clients cannot supply runtime adapter options, bindings, or rates.
    - `enabled` boolean, required
    - `scope` 'selected' | 'all': Valid only when enabled. Omit for selected compatibility. All is expanded only at explicit publication and can be unavailable when a provider candidate limit is smaller than the compatible set.
  - `language` string
  - `language_profiles` AgentLanguageProfileOverride[]: When supplied this replaces the sparse override set; omit it to preserve overrides. Each item has locale and optional exact voice, transcriber, and greeting. Omitted values inherit the shared stack; an empty greeting removes its override.
    - `greeting` string
    - `locale` string, required
    - `transcriber` object
      - `model` string, required
      - `provider` string, required
    - `voice` object
      - `model` string, required
      - `provider` string, required
      - `voice_id` string, required
  - `languages` string[]: The languages this agent may answer in. An engine without a language axis (see voice_frontend and the agent-catalog conversation_engines block) REFUSES this field with 400 rather than accepting and dropping it.
  - `mode` 'external_audio' | 'external_text' | 'hosted', required
  - `name` string
  - `opening` AgentOpening: Outbound opening policy shared across conversation engines. Only managed outbound telephone calls use this wait; inbound and browser tests retain their normal opening. Saved by voice-stack PATCH and frozen in each call publication. Empty object resets to callee-first with two seconds of observed silence. Speech is allowed to finish beyond this wait. The timer authorizes the opening request, not first audible sound; provider generation and network latency add time.
    - `first_speaker` 'callee' | 'agent'
    - `silence_wait_seconds` number: Silence before requesting the first message when first_speaker is callee. Preserved but unused with agent-first.
  - `organization_id` string, required
  - `project_id` string, required
  - `recording_enabled` boolean
  - `system_prompt` string
  - `tools` AgentTool[]
    - union: A legacy string placeholder, a built-in tool, a signed webhook tool, or an attached Integration Action. The names `press_digits`, `wait_on_hold` and `abandon_navigation` are reserved for phone-menu navigation and refused with 400.
      - string: Legacy tool placeholder.
      - BuiltinAgentTool: A built-in tool the AI agent can invoke during a call. `phone_number` is conditionally required: see its own description.
        - `connection_id` string: Required for calendar builtins: one active, owner-configured calendar connection in the AI agent's organization/project. The model cannot select it.
        - `country` string
        - `name` 'end_call' | 'send_sms' | 'transfer_to_human' | 'request_callback' | 'set_call_disposition' | 'calendar_check_availability' | 'calendar_book_appointment', required
        - `phone_number` string: Destination for `transfer_to_human`, and required when that tool is enabled. Must be full E.164 including the leading `+` and country code (for example `+12025550100`); a blank, partial, or non-dialable number is rejected when the agent is created or updated, rather than failing mid-call. Ignored by `end_call` and `send_sms`.
        - `ref` string: Read-only, server-issued. A stable opaque identifier for this configured tool, used to name the tool on a call's transcript rows without storing the tool's own name with them. Ignored if you send it: the value is taken from what is already stored, so it cannot be chosen. It survives any edit that keeps the tool's `name` and `type`; renaming, retyping or removing a tool retires its identifier, and transcript rows recorded before that show the tool's kind instead of a name.
        - `type` 'builtin', required
      - WebhookAgentTool
        - `description` string
        - `name` string, required
        - `parameters` object
        - `ref` string: Read-only, server-issued. A stable opaque identifier for this configured tool, used to name the tool on a call's transcript rows without storing the tool's own name with them. Ignored if you send it: the value is taken from what is already stored, so it cannot be chosen. It survives any edit that keeps the tool's `name` and `type`; renaming, retyping or removing a tool retires its identifier, and transcript rows recorded before that show the tool's kind instead of a name.
        - `type` 'webhook', required
        - `url` string, uri, required
      - IntegrationActionAttachment: Explicitly attaches one existing Integration Action to this agent. The model cannot choose another action or endpoint.
        - `connection_id` string, required
        - `name` string, required
        - `ref` string: Read-only, server-issued. A stable opaque identifier for this configured tool, used to name the tool on a call's transcript rows without storing the tool's own name with them. Ignored if you send it: the value is taken from what is already stored, so it cannot be chosen. It survives any edit that keeps the tool's `name` and `type`; renaming, retyping or removing a tool retires its identifier, and transcript rows recorded before that show the tool's kind instead of a name.
        - `tool_id` string, required
        - `type` 'connection', required
  - `translation` TranslationPolicy
    - `enabled` boolean
    - `target_languages` string[]
  - `voice` union
    - string: Legacy pinned voice identifier.
    - object: Exact selected TTS provider/model/voice inventory item.
      - `model` string, required
      - `provider` string, required
      - `voice_id` string: Provider inventory id, when the selected provider exposes voices.
  - `webhook_url` string

## Response `201`

Agent. agent_endpoint.webhook_secret carries the plaintext secret that signs every event and tool call sent to this agent's webhook (X-Agent-Telco-Signature), revealed once in this response only; every later read returns [REDACTED]. Save it immediately.

## Other responses

- `400`: organization_id or project_id is missing or blank; an unrecognized mode; a tools value that is neither empty nor a list; a metadata value that is neither empty nor an object; an external_audio or external_text agent missing its webhook_url/audio_stream_url; or a project_id this organization does not own. On create (unlike PATCH) a falsy tools or metadata -- null, false, 0, "" -- is read as an empty selection rather than refused

---

[API](https://skmtc.dev/skysay/apis/skysay-api.md) · [All operations](https://skmtc.dev/skysay/apis/skysay-api/llms.txt) · [OpenAPI document](https://skmtc.dev/skysay/apis/skysay-api/revisions/148c7e111260?raw)
