> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://poly-ai.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://poly-ai.ferndocs.com/_mcp/server.

# Return values

> Control agent behavior with strings, utterances, handoffs, and other return types from functions.

**This page requires Python familiarity.** It covers the return interface for functions written in Python.

Return a string or dictionary from your function to control the agent's next action, including what to say, whether to transfer the call, or how to configure listening behavior.

### String return

You can return a simple string, which will be used as the system prompt for the virtual agent:

```python
return "Tell the user that you cannot assist with their request"
```

### Dictionary return

You can return a dictionary to specify more detailed and deterministic instructions. The following fields can be used individually or in combination.

#### `content`

Equivalent to returning a string, this field specifies the system prompt:

```python
return {
  "content": "Tell the user that you cannot assist with their request"
}
```

#### `utterance`

Specifies the exact phrase the virtual agent will deliver after executing the function (spoken for voice, displayed for webchat):

```python
return {
  "utterance": "I cannot assist with your request."
}
```

**Note**: If both `content` and `utterance` are returned:

* The agent will stream the `utterance` to the user and end the turn.
* The `content` rules will apply to the next turn.

#### `utterance_channel`

Controls how the `utterance` is delivered. Defaults to `"SPEECH"`, which speaks the utterance through TTS. Set to `"DTMF"` to transmit the utterance as DTMF keypad tones instead of speech – useful when the agent needs to send digits to an IVR or other automated system during [outbound calling](/agent-studio/voice/numbers/outbound-calling).

The `utterance` string must contain the digits to send (`0`–`9`, `*`, `#`). The agent does not speak them.

```python
return {
  "utterance": "1234#",
  "utterance_channel": "DTMF"
}
```

> **Note**
>
> `utterance_channel` only takes effect when `utterance` is also set. The call must be running on a telephony channel that supports DTMF output – it is not available in webchat or with the realtime model.

#### `handoff`

Initiates a call handoff after the function executes. The `type` and `reason` fields match your configured [handoff destination](/agent-studio/voice/handoffs). The nested object specifies the SIP method.

#### SIP REFER (default)

Transfers the call and drops PolyAI from it:

```python
return {
  "handoff": {
    "type": "CALL_CENTER",
    "reason": "SPEAK_TO",
    "refer": {
      "phone_number": "12345"
    }
  }
}
```

#### SIP INVITE

Creates a bridged call where PolyAI stays on the line between both parties:

```python
return {
  "handoff": {
    "type": "CALL_CENTER",
    "reason": "SPEAK_TO",
    "invite": {
      "phone_number": "2222222",
      "outbound_caller_id": conv.caller_number,
      "outbound_endpoint": "<ADD_YOUR_OUTBOUND_ENDPOINT_NAME>",
      "sip_headers": {
        "X-ANY_HEADER_NAME": "header"
      }
    }
  }
}
```

#### SIP BYE

Signals that the PolyAI call leg is over, returning control to the client's SBC:

```python
return {
  "handoff": {
    "type": "CALL_CENTER",
    "reason": "CONTAINED",
    "bye": {
      "sip_headers": {
        "X-Outcome": "contained"
      }
    }
  }
}
```

You can also trigger handoffs using [`conv.call_handoff()`](/agent-studio/tools/classes-and-objects/object), which supports dynamic `destination`, `reason`, and `utterance` parameters. Use the dictionary return for fine-grained SIP control; use `conv.call_handoff()` for simpler routing.

#### `hangup`

Ends the conversation after the function executes. For voice, this disconnects the call; for webchat, this closes the session:

```python
return {
  "hangup": True
}
```

#### `listen`

Configures the agent to listen on the next turn. Must be combined with an `utterance` for ASR timeout settings to take effect.

```python
return {
  "utterance": "Can I help you with anything else?",
  "listen": {
    "asr": {
      "timeout": 20
    }
  }
}
```

The `listen` object supports these configuration keys:

| Key                | Type | Description                                                                                                                  |
| ------------------ | ---- | ---------------------------------------------------------------------------------------------------------------------------- |
| `listen`           | bool | Whether to listen for input (default `True`)                                                                                 |
| `delayed_response` | bool | Enable delayed response mode – the client sends an empty input to retrieve the response                                      |
| `asr`              | dict | ASR configuration: `timeout`, `keywords`, `custom_biases`, `fields`, `corrections`                                           |
| `dtmf`             | dict | DTMF configuration: `num_digits`, `first_digit_timeout`, `inter_digit_timeout`, `finish_on_key`, `early_listening`, `is_pii` |
| `channel`          | str  | Input channel: `"SPEECH"` (default), `"DTMF"`, or `"SPEECH_AND_DTMF"`                                                        |
| `barge_in`         | dict | Barge-in configuration: `is_enabled`, `interruption_window`                                                                  |
| `smart_vad`        | dict | Smart VAD configuration: `is_enabled`, `max_extensions`, `max_extension_duration`                                            |
| `interjection`     | dict | Interjection configuration: `enable`, `frequency` (seconds)                                                                  |

**Example with DTMF:**

```python
return {
  "utterance": "Please enter your 6-digit code followed by the hash key.",
  "listen": {
    "channel": "SPEECH_AND_DTMF",
    "dtmf": {
      "num_digits": 6,
      "finish_on_key": "#",
      "first_digit_timeout": 5,
      "inter_digit_timeout": 2,
      "is_pii": True
    }
  }
}
```

> **Note**
>
> ASR timeout doesn't apply to DTMF-only input. If using both channels, make sure you include an `utterance` so the ASR settings take effect.

#### `variant`

Switches the conversation to a different variant. The value must match an existing variant name configured in [Variants](/agent-studio/knowledge/variants/overview). Use this to route callers to location-specific or segment-specific conversation flows mid-call.

```python
return {
  "variant": "orleans"
}
```

You can combine `variant` with `utterance` to confirm the switch to the user:

```python
return {
  "utterance": "Let me connect you with the Orleans location.",
  "variant": "orleans"
}
```

## Combining return fields

You can return multiple fields together. Common combinations:

#### utterance + handoff

Say something before transferring – the most common handoff pattern:

```python
return {
  "utterance": "Let me transfer you to a specialist who can help with that.",
  "handoff": {
    "type": "CALL_CENTER",
    "reason": "SPEAK_TO",
    "refer": { "phone_number": "12345" }
  }
}
```

#### utterance + hangup

Say a closing message before ending the call:

```python
return {
  "utterance": "Thanks for calling, have a great day!",
  "hangup": True
}
```

#### utterance + content

Deliver a specific phrase now and set instructions for the next turn:

```python
return {
  "utterance": "I've updated your reservation.",
  "content": "The reservation has been changed. Ask if there's anything else you can help with."
}
```

## Related pages

#### [Start tool](/agent-studio/tools/start-tool)

Return values supported in the start function.

#### [Delay control](/agent-studio/tools/delay-control)

Add filler phrases while long-running functions execute.

#### [Transition functions](/advanced/code-driven-flows/transition-functions)

Return values in the context of flow step routing.