---
title: "Create Phone Call"
method: POST
path: "/v1/agent/phone-calls"
tags: ["Phone Calls"]
---

# Create Phone Call

`POST /v1/agent/phone-calls`

## Request

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

Place an outbound call from one of your phone numbers. Platform numbers
dial US, Canada or Japan destinations, and a domestic trunk 0 after +81
(e.g. +81080...) is accepted and normalized to E.164 (+8180...).
Imported SIP numbers dial through their own termination and may attach
custom INVITE headers with `sip_headers`. Returns immediately with the
session queued for dialing. Subscribe to the `phone_call.dial_finished`
webhook or poll `GET /v1/agent/sessions/{session_id}` for the dial
outcome and the leg's `sip_call_id`. Ringing is never billed: metering
starts when the callee answers.

Errors carry a machine-readable `reason` (e.g. `destination_not_allowed`,
`insufficient_credit`, `concurrency_limit_exceeded`).

## Headers

- `idempotency-key` string, nullable

## Request body

- PhoneCallCreatePayload: POST phone-calls body, shared by the public and console routes. to_number is deliberately not format-validated here: the contract orders the E.164 check after the agent/published checks, so it runs in the service with a machine-readable reason.
  - `agent_id` string, required
  - `phone_number_id` string, required: The team-owned number to dial from (twilio provider only).
  - `to_number` string, required: Destination in E.164, e.g. +14155550123.
  - `dynamic_variables` object, nullable
  - `overrides` AgentSessionOverridesPayload: Wire twin of @fishaudio/agent-protocol `SessionOverrides` (session.ts). Every field must be allow-listed in Agent.overrides_allowed; unauthorized fields fail session creation loudly rather than being silently dropped.
    - `first_message` string, nullable
    - `first_message_prompt` string, nullable
    - `system_prompt` string, nullable: Limited to 32000 tokens (o200k_base), same budget as the configured prompt it replaces.
    - `voice_id` 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', nullable
    - `asr` AgentSessionAsrOverridesPayload: Per-session subset of the agent config's asr section, same field names.
      - `model` 'deepgram:nova-3' | 'elevenlabs:scribe_v2_realtime' | 'elevenlabs:scribe_v2_medical' | 'fish_audio:transcribe-stream-1', nullable: Speech recognition model for this session, same values as the agent's `asr.model`.
    - `llm` AgentSessionLlmOverridesPayload: Per-session subset of the agent config's llm section, same field names.
      - `fallback` AgentLLMFallbackPayload: Replaces the whole fallback: `mode` and `models` travel together.
        - `mode` 'platform' | 'manual' | 'off', required
        - `models` AgentLLMFallbackModelPayload[], 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` AgentLLMCustomPayload: Write model: full base_url policy + caps; stored values come only from here.
            - `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.
  - `metadata` object, nullable
  - `llm_extra_body` object, nullable
  - `sip_headers` object, nullable: Custom SIP headers for this call's INVITE, imported SIP numbers only. Same rules as the number's termination_headers, and merged over them.

## Response `201`

Document created, URL follows

- object
  - `session_id` string, required
  - `status` 'queued'

## Other responses

- `401`: No permission -- see authorization schemes
- `402`: No payment -- see charging schemes
- `403`: Request forbidden -- authorization will not help
- `404`: Nothing matches the given URI
- `409`: Request conflict
- `422`
- `429`: The user has sent too many requests in a given amount of time ("rate limiting")
- `502`: Invalid responses from another server/proxy
- `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/phone-calls/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)
