> ## Documentation Index
> Fetch the complete documentation index at: https://daily-ms-ws-body-url-encode.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# VoicemailDetector

> Reference for VoicemailDetector and TTSGate: classifier-based voicemail detection for outbound calls, with parameters and events.

## Overview

`VoicemailDetector` decides whether an outbound call reached a person or went to voicemail. It listens to what the other side says and asks a [classifier](/api-reference/server/classifiers/overview). Until it has a verdict, its `TTSGate` holds the bot's speech back, so a voicemail greeting is never talked over and a person hears the bot's reply as soon as the verdict is in.

For a guide to setting it up, see [Voicemail Detection](/pipecat/fundamentals/voicemail).

<CardGroup cols={2}>
  <Card title="Voicemail Detection Guide" icon="voicemail" href="/pipecat/fundamentals/voicemail">
    Set up voicemail detection and leave a message
  </Card>

  <Card title="Example Implementation" icon="code" href="https://github.com/pipecat-ai/pipecat/blob/main/examples/features/features-voicemail-detection.py">
    A complete outbound bot with voicemail detection
  </Card>
</CardGroup>

```python theme={null}
from pipecat.extensions.voicemail.voicemail_detector import VoicemailDetector
```

## Configuration

<ParamField path="classifier" type="BaseClassifier" required>
  What decides between a person and a voicemail, such as a
  [`JevClassifier`](/api-reference/server/classifiers/jev) or an
  [`LLMClassifier`](/api-reference/server/classifiers/llm). The detector owns
  its lifecycle: it sets the classifier up and cleans it up with the pipeline.
</ParamField>

<ParamField path="decision_timeout" type="float" default="1.0">
  Seconds of silence after the caller stops speaking before the latest answer
  decides. A greeting resumes after its pauses while a person stops and waits,
  so a verdict on a fragment such as "hi, this is Sam" is not acted on until the
  caller has really stopped.
</ParamField>

<ParamField path="voicemail_response_delay" type="float" default="2.0">
  Seconds of silence after a voicemail verdict before `on_voicemail_detected`
  fires, so the message is left after the greeting ends and the recording
  starts. Speech, such as the rest of the greeting, restarts the wait.
</ParamField>

<ParamField path="llm" type="LLMService | None" default="None" deprecated>
  *Deprecated since 1.12.0, removed in 2.0.0.* An LLM service for the
  classification. It is wrapped in an `LLMClassifier`. Pass
  `classifier=LLMClassifier(llm=llm)` instead.
</ParamField>

<ParamField path="custom_system_prompt" type="str | None" default="None" deprecated>
  *Deprecated since 1.12.0, removed in 2.0.0.* A system prompt for the `llm`,
  placed in front of the `LLMClassifier`'s own instructions. Pass an
  `LLMClassifier` with your own `instructions` instead.
</ParamField>

## Methods

### detector

```python theme={null}
def detector() -> VoicemailDetector
```

The processor to place after the STT service and before the user context aggregator. It is the detector itself.

### gate

```python theme={null}
def gate() -> TTSGate
```

The `TTSGate` to place right after the TTS service. While the decision is pending it buffers TTS frames (`TTSStartedFrame`, `TTSTextFrame`, `TTSAudioRawFrame`, `TTSStoppedFrame`) and passes everything else. A conversation verdict releases the buffered frames in order. A voicemail verdict discards them, since they were meant for a person.

## How the Decision Is Made

1. After each transcription, the detector asks the classifier a choice question, `"conversation"` or `"voicemail"`, about the whole transcript so far. Transcriptions that arrive while a question is in flight are asked about together in the next one.
2. The detector keeps the latest answer but does not act on it while the caller is speaking.
3. Once the caller has been quiet for `decision_timeout` and any question in flight has been answered, the latest answer becomes the verdict. The verdict is final.
4. If the classifier never answered, because every call failed or timed out, the detector assumes a conversation, so a person is never left waiting on a bot that won't speak.

On a **conversation** verdict, the gate releases the held speech and `on_conversation_detected` fires.

On a **voicemail** verdict, the gate drops the held speech and the pipeline is interrupted. From then on the detector stops passing the other side's frames downstream (only system, end, stop and agent lifecycle frames still flow), so the greeting never reaches the conversation LLM. After `voicemail_response_delay` of silence, `on_voicemail_detected` fires, once.

## Event Handlers

| Event | Description |
| - | - |
| `on_conversation_detected` | A person answered. The held speech has been released. |
| `on_voicemail_detected` | The call went to voicemail and the greeting has been quiet for `voicemail_response_delay`. |

Both handlers receive the detector, which is a frame processor, so a handler can push frames through it:

```python theme={null}
@voicemail_detector.event_handler("on_conversation_detected")
async def on_conversation_detected(detector):
    logger.info("A person answered")


@voicemail_detector.event_handler("on_voicemail_detected")
async def on_voicemail_detected(detector):
    await detector.push_frame(TTSSpeakFrame("Hi, this is Alex. Please call me back."))
    await detector.push_frame(EndWorkerFrame())
```

## Metrics

The detector pushes its classifier's [`on_metrics`](/api-reference/server/classifiers/overview#on_metrics) data into the pipeline as a `MetricsFrame`, so the time each classification took (and, with `JevClassifier`, the tokens it used) shows up alongside the pipeline's other metrics.

## Requirements

* The detector is built for a cascaded pipeline: it reads the STT service's transcriptions and holds the TTS service's output.
* An `LLMClassifier` needs a service that supports `run_inference()`, so a realtime (speech-to-speech) LLM cannot back it.

## Removed in 1.12.0

The detector used to run a parallel pipeline with its own LLM. These parts of it were removed from `pipecat.extensions.voicemail.voicemail_detector`:

* `NotifierGate`, `ClassifierGate`, `ConversationGate` and `ClassificationProcessor`
* `VoicemailDetector.CLASSIFIER_RESPONSE_INSTRUCTION` and `VoicemailDetector.DEFAULT_SYSTEM_PROMPT`
