---
title: "Read a hosted agent's safe STT, model, and TTS configuration"
method: GET
path: "/v1/agents/{agent_id}/voice-stack"
---

# Read a hosted agent's safe STT, model, and TTS configuration

`GET /v1/agents/{agent_id}/voice-stack`

## Request

- Base URL: `https://api.skysay.ai`
- URL: `https://api.skysay.ai/v1/agents/{agent_id}/voice-stack`
- Auth: none declared

The target agent authorizes the tenant boundary. Returns selected/resolved STT, model, and TTS catalog metadata plus validation warnings, cost, latency, prompt, and tuning configuration. Platform-managed selections use the catalog's managed component estimate; a BYOK component selected on PATCH through an existing component credential ID has no Skysay component charge. Credential binding IDs, plaintext, ciphertext, nonce, fingerprints, and provider secrets are never returned. This GET is a pure read: it makes no provider call, validation probe, compilation, repair, lazy creation, or write.

## Path parameters

- `agent_id` string, required

## Response `200`

Safe voice-stack projection

- AgentVoiceStack: Safe read projection. It never includes component credential binding IDs, plaintext/ciphertext, nonce, fingerprint, or provider secrets.
  - `agent_id` string, required
  - `answer_detection` AgentAnswerDetection: What the platform does when a call the carrier reported answered turns out not to be a person: non-human audio such as a caller tune, or a voicemail box. Unlike the other tuning sections, a value you send here is STORED even when it equals the current default, and only an ABSENT `answer_detection` key means "follow the platform default". That is deliberate: the platform default for this setting is expected to change once the detector has been measured, and an agent that explicitly chose a value keeps it when that happens. Applied when a call starts; saving never changes an in-progress call.
    - `non_human_audio` 'shadow' | 'end_call' | 'off': `shadow` records what was heard on every answered call and changes nothing the caller experiences: the classification and its detail appear on the call, and no call is ever ended by it. `end_call` acts on the verdict: the agent stops speaking and the call is ended with `ended_reason: no_human_after_answer`, and the classification and its detail appear on the call as they do under `shadow`. `off` collects nothing, so such a call carries no classification at all. `shadow` is the platform default, so it is what an agent with no stored value gets; sending it explicitly stores it, which is how you keep it if that default later moves. This property carries no JSON Schema `default` on purpose, unlike the other tuning properties: a generated client that pre-filled one would send `shadow` on every save and pin a choice the customer never made, which is the one outcome the storage rule above exists to prevent.
    - `voicemail` 'shadow' | 'hang_up' | 'leave_message' | 'off': What the AI agent does when a voicemail box answers an outbound call. Voicemail is judged from the first seconds of audio after the answer; the phone network does not report it. Only mailbox-specific evidence counts as proof: a record beep that follows a spoken greeting, or a mailbox instruction such as "leave a message", from a language whose phrase list is verified (en; English is checked on every call). The beep works in every language. The greeting before it must reach three recognised words, counted by the spaces and punctuation between them, so in Chinese, Japanese, Thai and other scripts written without spaces a short greeting is undercounted, and a beep after it only raises suspicion. Candidate phrase lists (de, es, et, fr, ru) are not yet verified: what they recognise is shown as evidence and never proves a mailbox. In any other language only the beep or an English instruction can. A fast answer, a long greeting or music only raise suspicion, and a call the platform is unsure about is treated as a person. `shadow` (watch only) records the verdict and what the platform would have done on the call in Calls, and changes nothing the caller experiences. `hang_up` ends the call without a message on proof of a mailbox. `leave_message` plays the `voicemail_messages` entry for the call's language after the beep, once per contact, and hangs up without a message when there is none for that language. `off` detects nothing. `hang_up` and `leave_message` are accepted and stored now but currently run as `shadow`: they start acting only after the detector has passed its accuracy check, and each call's evidence shows both the chosen and the effective mode. `shadow` is the platform default; like `non_human_audio`, an explicitly sent value is stored even when it equals the default, and this property carries no JSON Schema `default`. The `answer_detection` object is replaced as a whole on save, so send every key you want to keep.
    - `voicemail_messages` object: The messages left when `voicemail` is `leave_message`, one per language, keyed by language code (`{"en": "...", "et": "..."}`); a locale such as `de-DE` is stored as `de`. `leave_message` requires at least one. The message played is the one for the language the call is actually in; a call in a language with no message is ended without one, never given a message in another language. Plain text; runs of whitespace are collapsed, a blank text removes that language's message, and control characters are refused. Each message is played exactly as approved, never improvised by the AI agent, so keep it neutral: who is calling and how to call back, with no account, amount or other personal details, because a voicemail box can be heard by someone other than the person you meant to reach. Messages are kept when `voicemail` changes to another mode and for languages the agent no longer offers.
  - `audio_environment` union
    - AudioEnvironmentSelection
      - `asset_version` 'v1', required: Immutable packaged asset version; it is never inferred.
      - `preset_id` string, required: Exact catalogue id. `off` has no asset.
      - `volume` integer, required: Whole-number selection. Off requires 0; enabled presets require 1 through 100.
    - AudioEnvironmentInvalidSelection: A historic malformed saved pin, returned visibly instead of silently becoming Off. Choose a supported selection or Off before publishing again.
      - `asset_version` string, required
      - `preset_id` string, required
      - `status` 'invalid', required
      - `volume` integer, nullable, required
  - `cost` object, required
  - `first_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, required
  - `language_profiles` AgentLanguageProfiles: Safe editor projection. The immutable runtime profile and credential binding IDs are never returned. Published Follow summaries expose only the frozen allowed locale set, opt-in bit, and opening locale for the authenticated test picker.
    - `languages` string[], required
    - `profiles` AgentLanguageProfileOverride[], required
      - `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
    - `published_at` string, required: ISO 8601 publication time. An empty string with published_version 0 is the legacy-agent sentinel: no language-profile publication exists yet.
    - `published_follow_caller` AgentFollowCaller, required: 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.
    - `published_languages` string[], required: Locales from the immutable published profile set. This is empty when no publication exists.
    - `published_starting_language` string, required: Frozen Follow opening locale for the published browser-test picker, or an empty string when Follow is not enabled.
    - `published_version` integer, required
    - `revision` integer, required
  - `latency` object, required
  - `model` object, required
  - `navigation` AgentNavigationStatus: The agent's phone-menu navigation as it stands: the stored choice, what it resolves to, both limits, and whether its outbound calls may navigate. Always present on GET. `eligible` describes the agent, not one call: the call's direction, its reservation and the voice worker still decide each call, and each call's own evidence says what happened.
    - `effective` boolean, required: Whether the setting is on: the stored choice, or the platform default (on) when it is null.
    - `eligible` boolean, required: Whether this agent's outbound calls may navigate.
    - `enabled` boolean, nullable, required: The stored choice: `true`, `false`, or `null` when the agent never chose and follows the platform default.
    - `ineligible_reason` 'turned_off' | 'not_released' | 'engine_not_supported' | 'languages_unreleased' | 'languages_uncovered' | 'too_many_sockets' | 'no_language' | 'tool_name_conflict' | 'tool_budget' | 'null', nullable, required: Why `eligible` is false, else null: `turned_off` (`enabled` is false), `not_released` (navigation is switched off platform-wide), `engine_not_supported` (the agent's conversation engine, GPT-Live today, does not navigate yet), `languages_unreleased` (navigation is released in none of the agent's languages, such as an agent that speaks only Estonian or Finnish), `languages_uncovered` (no supported transcriber covers one of the agent's languages), `too_many_sockets` (covering the agent's languages would need a third transcriber connection), `no_language` (the agent has no language), `tool_name_conflict` (one of the agent's own tools uses a reserved navigation tool name), or `tool_budget` (the agent's own tools leave no room under the 32-name tool contract for the three navigation tools).
    - `max_hold_seconds` integer, required: The hold limit, with the default applied.
    - `max_navigation_seconds` integer, required: The menu limit, with the default applied.
  - `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
  - `selectable` boolean, required
  - `system_prompt` string
  - `transcriber` object, required
  - `turn_taking` AgentTurnTaking: Per-agent barge-in and endpointing tuning. Every field is optional and omitted from responses when it is at its default, so an agent that has never configured this section has no `turn_taking` key at all. Applied when a call starts; saving never changes an in-progress call.
    - `barge_in_speech_rms` number: Deprecated. Accepted, stored and returned for compatibility only; this runtime does not apply it to call behavior. Use `interruption_sensitivity`, `min_speech_ms` and `min_interruption_words` to control interruptions.
    - `interruption_sensitivity` 'balanced' | 'low' | 'high': Hosted phone calls in every language. Preset interruption threshold: balanced 500 ms (the default), low 750 ms, high 150 ms. Preset end-of-turn hold on a language with the trained turn detector, the wait when the detector judges the caller finished: balanced 600 ms (the default), low 900 ms, high 260 ms; and the longest wait when it judges them probably not finished, before the turn ends anyway: balanced 1200 ms (the default), low 1800 ms, high 600 ms. On a language without the detector, where the hold is the whole end-of-turn decision: balanced 800 ms (the default), low 1100 ms, high 600 ms. Phone lines get these longer holds than a browser test because a caller pausing mid-sentence (after their name, before a number, while leaving a message) must not have their turn ended for them; `low` holds longer still, and `high` replies sooner. An explicit `min_speech_ms` or `silence_hold_ms` takes precedence over the preset for its knob. The end-of-turn hold is a minimum wait, not the reply time: a turn never ends before voice detection reports the end of speech, which takes about 250 ms of silence, or before the final transcript arrives, which depends on the speech-to-text provider.
    - `max_call_seconds` integer: How long a call may run before the platform ends it and reports `ended_reason: max_call_duration_reached`. Defaults to 1740 (29 minutes), which is also the ceiling: the underlying media session is dropped at 30 minutes, and a limit above that could never fire, so the call would end with no reason attached. Lower it to cap a call sooner; a value outside the range is refused rather than clamped.
    - `min_interruption_words` integer: Defaults to 1 on hosted ENGLISH phone calls and to 0 everywhere else; set 0 explicitly to disable it. When set, an interruption must ALSO carry at least this many recognized words from the caller, in addition to the speech-duration threshold; it never lowers that threshold. On hosted ENGLISH calls, short listening noises ("mm-hm", "uh-huh", "yeah", "okay", "right", "sure", and filler words such as "uh") do not count toward it, so a caller showing they are following does not interrupt; every word counts in other languages. Not English-only, but effective only on stacks whose transcriber reports words fast enough to land inside the decision window (today the Deepgram stacks, English and Estonian). Elsewhere it is stored, returned unchanged, and ignored at call time.
    - `min_speech_ms` integer: Hosted phone calls in every language. Explicit continuous-speech threshold before an interruption is considered; it overrides any sensitivity preset. On a phone call the value is honoured down to 150 ms; a value below 150 ms is raised to 150 ms. A phone call with no value set and no sensitivity preset uses the 500 ms default. Browser and web calls do not apply this value at all: it is still stored and returned, but those calls keep the stack's own threshold.
    - `no_caller_speech_seconds` integer: How long after the call is answered the platform waits to hear the other party before ending the call and reporting `ended_reason: no_caller_speech_after_answer`. Defaults to 20. Speech is detected from the audio itself, not from a transcript, and once the other party has been heard once the rule does not apply again for the rest of that call. The floor is 10 because the witness is voice activity: a caller who is merely listening looks the same as one who is not there, and a shorter value ends the call partway through the agent's own opening line.
    - `silence_hold_ms` integer: Hosted phone calls in every language. The shortest wait after the caller stops speaking before their turn can end; it overrides the platform default hold and any sensitivity preset's shortest wait (the preset's longest wait still applies on a language with the trained turn detector, and is raised to this value when it is shorter). It is a minimum, not the reply time: a value below about 250 ms has no effect, because voice detection needs that much silence to report the end of speech, and the wait is longer whenever the final transcript arrives later than this value.
  - `voice` object, required: The stored TTS selection as the agent catalog projects it, including `delivery_control` (`voice_settings`, `instruction`, `speed` or `unsupported`): whether a `voice_polish.delivery` profile changes this voice's audio, and `delivery_profiles`: the exact value each profile resolves to on this engine (empty for `unsupported`), both read from the packaged delivery table.
  - `voice_frontend` 'cascade' | 'gpt_live' | 'gemini_live': The conversation engine; cascade is the default. Always present on GET; omitted on older rows means cascade. Whether this engine has a language axis is declared in the agent-catalog conversation_engines block (has_language_axis).
  - `voice_label` string
  - `voice_polish` AgentVoicePolish: Per-agent voice polish. Absent keys keep the platform defaults; the response omits the section entirely when nothing is configured.
    - `acknowledgements` AgentAcknowledgements: Optional short acknowledgement while a slow reply is still being prepared: at most one per caller turn, spoken in the agent's own voice, model and current language on a separate outgoing track, cancelled the moment the real reply starts. Off unless enabled. Without phrases the platform's reviewed list for the call language is used; a language without one stays silent rather than switching to English.
      - `delay_ms` integer: How long the reply may take before an acknowledgement is worth speaking.
      - `enabled` boolean
      - `phrases` string[]: Bounded neutral phrases; digits and currency marks are rejected so a phrase cannot carry a factual, payment, legal or consent claim.
    - `delivery` 'natural' | 'lively' | 'calm' | 'precise': How the selected voice performs its speech. Unset keeps the provider's own defaults, which is what every agent had before this field existed. Each profile maps to allowlisted provider controls only where the selected TTS provider/model truthfully exposes them: ElevenLabs voice settings (eleven_v3 supports stability only), a bounded Gemini TTS synthesis instruction, and a speaking-rate multiplier for Soniox and Cartesia. Fish Audio and Azure neural voices accept the setting but apply no change. The voice preview (POST /v1/voice-previews with the same `delivery`) uses the identical mapping, so an audition sounds like the call.
    - `end_of_call_phrase` string
    - `model_unresponsive` AgentModelUnresponsive: How long the agent may stay silent after the caller has finished speaking before the platform ends the call. It exists because a conversation engine can stop answering without saying so: a provider outage mid-call leaves the line up, the caller talking to nobody and the call billing, and on some engines nothing at all is reported. It counts only time the line was free: the clock starts when a caller turn ends, it does NOT run while the caller is speaking (their own speech is subtracted, so a caller who keeps talking is never cut off mid-sentence, and one who pauses between attempts still reaches the bound), and it is cleared the moment the agent produces anything — a reply, a tool call, or the end of the call. It also never runs while the agent is speaking, while a tool is running, before the caller's first turn, or on a call whose replies come from a workflow. A call ended this way reports `ended_reason` `agent_unresponsive` with `reason_detail` `model_unresponsive` (calls completed before 2026-09-25 reported `model_session_lost` with the same detail). On by default at the platform's own bound; an absent section is that default and is not stored.
      - `seconds` number: The bound, in seconds. `0` switches it off, and is the only value outside the range: an agent that waits on something genuinely slow may need it. Otherwise 10 to 120. The platform's own bound applies when unset.
    - `tool_wait` AgentToolWait: What the caller hears while a slow tool call (an integration action, a knowledge search) is still running. Off unless a mode is set. `sound` plays a quiet keyboard-typing bed on the separate outgoing track once the tool has run for `delay_ms`, cut the instant the tool finishes, capped at 15 seconds; it is wordless, so it works in every language. `acknowledge` speaks one short phrase in the agent's own voice and language instead; without phrases the platform's reviewed list for the call language is used, and a language without one stays silent. Feedback never plays over the caller or the agent, at most once per tool wait, and never for the built-in end-call or transfer tools.
      - `delay_ms` integer: How long a tool may run before feedback starts. Most tools finish well inside this.
      - `mode` 'off' | 'acknowledge' | 'sound'
      - `phrases` string[]: For `acknowledge` only; the same bounded neutral-phrase rule as acknowledgements. Rejected with `sound`.
    - `unrecognised_speech` AgentUnrecognisedSpeech: What the agent does when the caller speaks and the transcriber produces nothing at all — most often a caller using a language the agent's transcriber is not listening for, but also a bad line or a dropout. The agent says one short line in the call's own language telling the caller it could not understand them and which languages it can help in, then keeps listening; it never guesses what was said, never calls a tool and never ends the call. It is spoken only after the caller has stopped talking and the platform's wait for a transcript has expired, so it cannot talk over a live caller, and it is skipped entirely on an agent with follow-the-caller enabled, where the language switch answers instead. On by default, and spoken at most twice per call unless you change `max_per_call`. Send `enabled: false` to switch it off; an absent section is the default and is not stored.
      - `enabled` boolean: `false` switches the line off for this agent, and is the only value stored: the behaviour is on for every agent that says nothing.
      - `max_per_call` integer: How many times one call may hear it. Two when unset.
      - `phrase` string: Your own wording, spoken verbatim in place of the line the agent would otherwise compose in the call's language. Write it in the agent's language: it is not translated.
  - `warnings` string[], required

## Other responses

- `404`: Agent not found

---

[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)
