> ## Documentation Index
> Fetch the complete documentation index at: https://docs.poly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables

> Store and access data across conversation turns using the conv.state object in functions.

> [!NOTE]
>
> **This page requires Python familiarity.** Variables are set and read in Python functions.

Variables are defined by setting a property on the `conv.state` object within a function. You can choose a name for your variable and update its value to anything you want like so:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
conv.state.my_variable = 1
conv.state.my_other_variable = {
    "property": "value"
}
```

These variables will retain their state between turns of the conversation and can be referenced in subsequent tool calls like so:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
if conv.state.my_variable == 10:
    return "Well done you hit 10"
```

### Reading variables that haven't been set

Reading a `conv.state` variable that was never assigned returns Python `None` – it does **not** raise `AttributeError` or `KeyError`. Both attribute and dictionary access are supported and equivalent:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
conv.state.foo            # None if never set
conv.state["foo"]         # None if never set
conv.state.get("foo")     # None if never set (with optional default)
```

This enables a few common patterns:

**Defensive defaults with `or`** – use when any falsy value should be replaced:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
attempts = conv.state.verification_attempts or 0
intent = conv.state.call_intent or "other"
```

Note: `or` cannot distinguish "never set" from a stored falsy value (`0`, `""`, `False`, `None`).

**Explicit unset check with `is None`** – use when the variable is intentionally initialized to `None` in the [start tool](https://docs.poly.ai/tools/start-tool.md) as a marker that no value has been collected yet:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
def start_function(conv: Conversation):
    conv.state.booking_time = None   # explicit "not yet collected" marker
    conv.state.customer_id = None

# later, in a flow step
if conv.state.booking_time is None:
    return "What time would you like to book?"
```

**Strict "key was assigned at all" check with `in`** – use when you must distinguish "never assigned" from a stored `None`:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
if "booking_time" in conv.state:
    ...
```

Initializing variables to `None` (or a sensible default like `""` or `0`) in `start_function` is recommended whenever a variable is referenced in a [prompt template](#prompt-templating) or read in a flow step before it is guaranteed to be set, so template references and conditional checks always resolve predictably.

### Built-in state keys

The `conv.state` object comes pre-populated with system-managed keys. These are set automatically by the platform and available in every conversation:

| Key | Type | Description |
| - | - | - |
| `from_` | str | Caller's phone number (note the trailing underscore – `from` is a Python reserved word) |
| `to` | str | Called number (callee) |
| `call_sid` | str | Unique call session identifier |
| `asr_lang_code` | str | Active ASR language code (e.g., `"en-US"`) |
| `tts_lang_code` | str | Active TTS language code (e.g., `"en-US"`) |
| `shared_id` | str | Shared identifier for correlating conversations across systems |
| `handoff` | object | Handoff configuration object (destination, reason, SIP method) |
| `disable_recordings` | bool | Whether call recording is disabled |
| `stop_recording` | bool | Whether recording has been stopped mid-call |
| `use_tts_for_responses` | bool | Whether to use TTS instead of pre-recorded audio |
| `asr_provider` | str or dict | Override the default ASR provider |
| `asr_config` | dict | Custom ASR configuration |
| `listen_for_sms` | bool | Whether SMS listening is enabled (default `False`) |

> [!WARNING]
>
> `handoff_reason` and `handoff_number` are deprecated. Use the `handoff` object instead, which supports structured SIP configurations (REFER, INVITE, BYE).

You can read these values in any function:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
log.info(f"Caller: {conv.state.from_}, Callee: {conv.state.to}")
log.info(f"ASR language: {conv.state.asr_lang_code}")
```

### Prompt templating

Variables can be used inside tool calls and injected dynamically into prompts shown to the LLM. To inject a variable's value
into your prompt, use the syntax `$variable_name`. The system will replace this placeholder with the variable's value wherever it matches.

**Example:**

In your start function, you can write:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from datetime import datetime

def start_function(conv: Conversation):
    conv.state.current_date = datetime.now().strftime("%B %d, %Y")
```

**...then in your prompting:**

The current date is `$current_date`.

**...becomes:**

The current date is September 06, 2024.

When using variable templating, ensure the stored value is readable by the LLM. Complex objects like dictionaries or datetime
will be stringified automatically.

### Environment configuration

You can use the `conv.env` property to define environment-specific functions and activate test features in Sandbox or
Staging (`pre-release` in code).

For example:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
if conv.env == "sandbox":
    # enable early feedback flow
    pass
elif conv.env == "pre-release":
    # activate staging tools
    pass
elif conv.env == "live":
    # run production features
    pass
```

### Dynamic updates

The value templated into the prompt is always kept up to date, so any updates will be reflected in the next turn sent to the LLM.

**Example:**

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from datetime import datetime

def set_fake_date(conv: Conversation):
    conv.state.current_date = datetime(1995, 3, 22).date().strftime("%B %d, %Y")
```

After running this function, the resulting prompt would display:

The current date is March 22, 1995.

...in the next turn.

### Deleting a variable

To delete a variable, remove it from the state within a function:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
del conv.state['variable_name']
```

## Variables in the Conversations API

All `conv.state` variables – both built-in keys and custom variables your agent writes – are returned in the `state` field of the [Conversations API](https://docs.poly.ai/api-reference/conversations/introduction.md) response. This means you can retrieve any state variable programmatically after a call ends.

For details on the API response structure, see the [Conversations API overview](https://docs.poly.ai/api-reference/conversations/introduction.md#response-structure).

## Related pages

> [card] [conv object](https://docs.poly.ai/tools/classes/conv-object.md)
>
> Full reference for conv.state and other conversation properties.

> [card] [Start tool](https://docs.poly.ai/tools/start-tool.md)
>
> Initialize state variables before the greeting plays.

> [card] [Flows](https://docs.poly.ai/flows/introduction.md)
>
> Use variables to pass data across flow steps.
