> 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.

# Conversation

> The Conversation object, its attributes, and methods.

**This page requires Python familiarity.** It is a reference for developers writing functions inside Agent Studio.

The Conversation object (conv) provides access to conversation data and tools for managing the agent's behavior. It handles state management, flow transitions, SMS interactions, environment details, and voice selection.

## Attributes

#### id

**Description**: Unique identifier of the conversation.

**Example**:

```python
log.info(f"Conversation ID: {conv.id}")
# Example output: "conv_abc123xyz789"
```

#### account_id

**Description**: PolyAI account ID (the **Workspace ID**, prefixed with `ws-`) that owns this project. This is the same account ID visible in your Studio URL: `https://studio.<region>.poly.ai/<account_id>/...` (where `<region>` is `us`, `uk`, or `eu` — Agent Studio is region-specific).

**Example**:

```python
log.info(f"Account: {conv.account_id}")
# Example output: "acc_abc123"
```

#### project_id

**Description**: Project ID of the current agent. This is the same project ID visible in your Studio URL: `https://studio.<region>.poly.ai/<account_id>/<project_id>/...` (where `<region>` is `us`, `uk`, or `eu` — Agent Studio is region-specific).

**Example**:

```python
log.info(f"Project ID: {conv.project_id}")
# Example output: "proj_abc123"

if conv.project_id == "proj_123":
  print("Running in the main deployment")
```

#### env

**Description**: Current environment.

**Values**: "sandbox", "pre-release", "live"

**Example**:

```python
if conv.env == "live":
  log.info("Production traffic")
```

#### channel_type

**Description**: Type of channel the conversation is taking place on.

**Possible values**:

* `"sip.polyai"` – Voice calls (telephony/SIP)
* `"webchat.polyai"` – Webchat widget
* `"chat.polyai"` – Agent Studio in-browser chat
* `"sms.twilio"` – SMS text messages

> **Note**
>
> Use `.startswith()` for broader matching (e.g., `conv.channel_type.startswith("sms")`) when you don't need to distinguish between SMS sub-types.

**Example**:

```python
if conv.channel_type == "sip.polyai":
  log.info("Running a voice call")
elif conv.channel_type.startswith("sms"):
  log.info("Running an SMS conversation")
elif conv.channel_type == "webchat.polyai":
  log.info("Running a webchat conversation")
```

#### attachments

**Description**: List of `Attachment` objects queued to be included with the next agent message (`list[Attachment]`). Append to it with [`conv.add_attachments(...)`](#add_attachments). Attachments are only supported on **webchat** channels.

**Attachment object structure**:

* `content_url` (str) – URL to the main content of the attachment
* `content_type` (str) – The type of the attachment (`"image"`, `"weblink"`, or `"unspecified"`)
* `title` (str, optional) – Title of the attachment
* `preview_image_url` (str, optional) – URL to a preview image for the attachment
* `call_to_action` (str, optional) – Text for the call-to-action button or link

**Example**:

```python
for file in conv.attachments:
  print(f"Type: {file.content_type}")
  print(f"URL: {file.content_url}")
  if file.title:
    print(f"Title: {file.title}")
  if file.preview_image_url:
    print(f"Preview: {file.preview_image_url}")
```

#### sip_headers

**Description**: Dictionary of SIP headers (dict\[str, str]) provided by the carrier.

**Example**:

```python
source_ip = conv.sip_headers.get("X-Src-IP")
```

#### integration_attributes

**Description**: Metadata passed from an external integration (dict\[str, Any]). These attributes are defined per project and per integration during setup.

**Supported integrations**:

* [DNIs Pooling](https://docs.poly.ai/integrations/voice/dnis-pool) – Provides `shared_id` for correlating conversation outcomes

**Best practice**: Validate and extract required attributes in the `start_function` to handle missing data appropriately.

**Example**:

```python
# In start_function
if shared_id := conv.integration_attributes.get("shared_id"):
  conv.state.shared_id = shared_id
  log.info(f"Tracking with shared_id: {shared_id}")
else:
  # Handle missing integration data
  log.warning("No shared_id provided by integration")
```

#### caller_number

**Description**: The caller's identifier.

**For inbound calls**: The phone number of the person calling in, in [E.164](http://twilio.com/docs/glossary/what-e164) format (e.g., `+14155551234` for USA numbers).

**For outbound calls**: The phone number being called by the agent.

**For chat channels**: This may be an email address or integration-provided user ID.

**Example**:

```python
identifier = conv.caller_number
log.info(f"Caller identity: {identifier}")
# Inbound example: "+14155551234"
# Chat example: "user@example.com"
```

#### callee_number

**Description**: Number dialled by the caller.

**Example**:

```python
if conv.callee_number.endswith("1001"):
  conv.state.branch = "Priority"
```

#### state

**Description**: Dictionary-like store that persists values across turns. Validated entities are also written here under their entity name, coerced to their native Python type (`int` for numeric/quantity entities, `float` for decimal numerics and currency, `str` for everything else).

**Access patterns**: `conv.state` supports both bracket and attribute access — pick whichever reads more clearly. Reads and writes behave identically across the two styles:

```python
# Writes are equivalent
conv.state["is_verified"] = True
conv.state.is_verified = True

# Reads differ for missing keys
conv.state["is_verified"]        # raises KeyError if missing
conv.state.is_verified           # returns None if missing
conv.state.get("is_verified")    # returns None if missing
```

> **Warning**
>
> Missing-key behavior differs by access style:
>
> * `conv.state["missing_key"]` raises `KeyError`.
> * `conv.state.missing_key` returns `None` — it does **not** raise `AttributeError`.
> * `conv.state.get("missing_key")` returns `None` (or your default).
>
> This means `if conv.state.is_verified == False:` is **not** the same as "missing or False" — when the key is missing the value is `None`, and `None == False` is `False`, so the branch is skipped. Prefer `if not conv.state.get("is_verified"):` to treat missing and falsy the same way.

**Example**:

```python
# Manual state
conv.state["attempts"] = conv.state.get("attempts", 0) + 1

# Auto-synced entity, available as a native int
if conv.state.party_size > 15:
  flow.goto_step("Group Booking")

# Guarding a function on a verification flag
if not conv.state.get("is_verified"):
  conv.state.original_topic = "check_account_balance"
  conv.goto_flow("Identify & Verify User")
  return
```

#### current_flow

**Description**: Name of the flow currently executing, or None.

#### current_step

**Description**: Step name currently executing in the active flow.

**Example**:

```python
log.info(f"Current step: {conv.current_step}")
```

#### sms_queue

**Description**: List of `OutgoingSMS` / `OutgoingSMSTemplate` objects queued for dispatch at turn end.

#### variant_name

**Description**: Name of the active variant, or None.

#### variants

**Description**: Dictionary of all variant definitions (dict\[str, Variant]). Each key is the variant name and each value is a Variant object whose attributes match the columns defined in **Knowledge > Variants**.

**Example**:

```python
# Iterate variants to find one matching a custom attribute
for variant_name, variant in conv.variants.items():
  log.info(f"{variant_name}: {variant.phone_number}")

# Build a lookup from a variant attribute to variant name
callee_map = {
    variant.callee: name
    for name, variant in conv.variants.items()
}
matched = callee_map.get(conv.callee_number)
if matched:
    conv.set_variant(matched)
```

#### variant

**Description**: Variant object for the active variant, or `None` if no variant has been set. Attribute values are returned as **strings** (the storage type used by Variants) — parse them yourself if you need structured data. Reading an attribute that doesn't exist on the active variant returns `None`, not an `AttributeError`.

**Example**:

```python
if conv.variant:
  print(conv.variant.description)

  # Attribute values are strings — parse JSON yourself if needed
  import json
  hours = json.loads(conv.variant.opening_hours_json or "{}")
```

#### sms_templates

**Description**: Dictionary of SMS templates (dict\[str, SMSTemplate]).

**Example**:

```python
template_body = conv.sms_templates["booking_confirmation"].content
```

#### voice_change

**Description**: Pending TTSVoice change requested this turn, or None.

#### language

**Description**: Language code configured for the project, which may include a locale suffix (e.g. "en-US", "en-GB", "es-ES").

#### history

**Description**: Chronological list of UserInput and AgentResponse events so far.

**Example**:

```python
for event in conv.history:
  print(event.role, event.text)

# Example output:
# user "I need to book a table"
# agent "I'd be happy to help you book a table"
# user "For 4 people at 7pm"
```

#### handoffs

**Description**: Dictionary of configured hand-off destinations (dict\[str, HandoffConfig]).

**Example**:

```python
if "support" in conv.handoffs:
  print("Support line is available")
```

#### transcript_alternatives

**Description**: List of transcription alternatives (list\[str]) for the last user utterance, including the primary transcription.

**Example**:

```python
for alt in conv.transcript_alternatives:
  print(f"Alternative: {alt}")

# Example output:
# Alternative: "book a table for two"
# Alternative: "book a table for too"
# Alternative: "book a table for to"
```

#### real_time_config

**Description**: Returns a dictionary of real-time configuration values defined in Configuration Builder.

**Example**:

```python
config = conv.real_time_config
if config.get("after_hours_enabled"):
  conv.say("Our offices are currently closed.")
```

#### memory

**Description**: Dictionary of memory fields previously stored for the caller, retrieved from Agent Memory.

**Customer identification**: Memory is retrieved using `caller_number` for voice calls or the integration-provided user identifier for chat channels. This lets the agent recognize returning customers and recall previous interactions.

**Example**:

```python
cheese = conv.memory.get("cheese_type")
if cheese:
  conv.say(f"You're a fan of {cheese}, right?")

# Check if this is a returning customer
if conv.memory.get("last_order_date"):
  conv.say("Welcome back!")
```

#### entities

**Description**: Dictionary of entity validation results collected from the conversation (dict\[str, EntityValidationResult]). Each result's `.value` is the raw string captured from the caller.

For a typed value (`int`, `float`, or `str`) read the entity from [`conv.state`](#state) instead -- validated entities are auto-synced there under their entity name.

**Example**:

```python
if "email" in conv.entities:
  validated_email = conv.entities.email.value  # str

# Numeric entity as a native int
party_size = conv.state.party_size
```

#### functions

**Description**: Executor for calling other functions defined in the project. Access functions using dot notation.

**Example**:

```python
result = conv.functions.lookup_order()
```

#### api

**Description**: Executor for calling configured API integrations. Access APIs using `conv.api.{integration_name}.{operation_name}()`.
**Example**:

```python
response = conv.api.salesforce.get_contact(user_id="123")
if response.status_code == 200:
  contact_data = response.json()
```

#### integrations

**Description**: Executor for calling pre-built third-party integrations (e.g., OpenTable, Tripleseat) configured from **Integrations**. Access using `conv.integrations.{integration_name}.{method}()`.

> **Note**
>
> `conv.integrations` is for integrations PolyAI has built and maintains (proxied through Paragon). For custom HTTP APIs you define yourself in the **APIs** tab, use [`conv.api`](#api).

**Example**:

```python
# OpenTable
response = conv.integrations.opentable.get_reservation(confirmation_number="ABC123")

# Tripleseat
response = conv.integrations.tripleseat.create_lead(
  public_key=conv.utils.get_secret("tripleseat_public_key"),
  first_name="Jane",
  last_name="Smith",
  phone_number=conv.caller_number,
  location_id="12345",
)
if response.status_code == 200:
  lead_data = response.json()
```

See [Integrations](/integrations/integrations/overview) for the list of available integrations and setup instructions.

#### webchat

**Description**: Webchat-specific interface for functionality that only applies on `webchat.polyai` channels. Access webchat-only methods via `conv.webchat.{method}()`.

**Available methods**:

* `set_chat_call_actions(actions)` – attach click-to-call buttons to the next agent message.

**Example**:

```python
from polyai.webchat import ChatCallAction

if conv.channel_type == "webchat.polyai":
  conv.webchat.set_chat_call_actions([
    ChatCallAction(contact_number="+15551234567", title="Call support")
  ])
```

Calling webchat methods on non-webchat channels has no effect on the conversation, but you should still gate them on `conv.channel_type` for clarity.

#### generic_external_events

**Description**: List of external events initiated by `generate_external_event`. Each event contains `ext_event_id`, `send_to_llm`, `created_at`, `data`, and `content_type`.

**Example**:

```python
for event in conv.generic_external_events:
  if event.data:
    log.info(f"Received webhook data: {event.data}")
```

#### translations

**Description**: Proxy for accessing localized translations. Access translation keys as attributes to get the translated text for the current language.

**Example**:

```python
greeting = conv.translations.welcome_message
conv.say(greeting)
```

#### response_suggestions

**Description**: List of quick-reply suggestions for the next agent message. Only supported on webchat channels.

**Example**:

```python
for suggestion in conv.response_suggestions:
  print(suggestion)
```

#### agentic_dial

**Description**: Agentic dial data for the conversation, used for advanced dialing scenarios.

## Methods

#### say

**Description**: Override the next utterance.

**Example**:

```python
conv.say("I've made that change for you.")
```

#### randomize_voice

**Description**: Randomly choose a voice based on weighted probabilities.

**Parameters**:

* voice_weightings (list\[VoiceWeighting]) – list of VoiceWeighting objects, each containing a voice and weight.

**Available voices**: Import from `polyai.voice` module (e.g., `ElevenLabsVoice`, `CartesiaVoice`, `PlayHTVoice`, `RimeVoice`). See [Voice classes](/agent-studio/tools/classes-and-objects/voice) for the full list of available voices and their IDs.

**Example**:

```python
from polyai.voice import VoiceWeighting, ElevenLabsVoice

conv.randomize_voice([
  VoiceWeighting(voice=ElevenLabsVoice(provider_voice_id="voice1"), weight=0.7), 
  VoiceWeighting(voice=ElevenLabsVoice(provider_voice_id="voice2"), weight=0.3)
])
```

#### goto_flow

**Description**: Transition to another flow at turn end.

**Example**:

```python
conv.goto_flow("verification")
```

#### exit_flow

**Description**: Exit the current flow.

**Example**:

```python
conv.exit_flow()
```

#### set_variant

**Description**: Manually set the active variant.

**Example**:

```python
conv.set_variant("evening")
```

#### add_attachments

**Description**: Attach one or more visual tiles (images or web links) to the conversation. Only supported on webchat channels.

**Parameters**: attachments (list\[Attachment]) – list of Attachment objects.

**Attachment fields**:

* `content_url` (str) – URL to the main content of the attachment
* `content_type` (str) – The type of the attachment (`"image"`, `"weblink"`, or `"unspecified"`)
* `title` (str, optional) – Title of the attachment
* `preview_image_url` (str, optional) – URL to a preview image for the attachment
* `call_to_action` (str, optional) – Text for the call-to-action button or link

**Attachment types**:

* `"weblink"` – Displays the title, preview image, and call-to-action text. Clicking navigates the user to `content_url`.
* `"image"` – Displays the title (if present), but no call-to-action. `preview_image_url` should be a lower resolution image, and `content_url` should be the full resolution image. Clicking shows the higher resolution version.
* `"unspecified"` – No specific rendering behaviour is applied.

**Example**:

```python
from polyai.attachments import Attachment

conv.add_attachments([
  Attachment(
    content_url="https://example.com/menu",
    content_type="weblink",
    title="View our menu",
    preview_image_url="https://example.com/menu-preview.jpg",
    call_to_action="Open menu"
  ),
  Attachment(
    content_url="https://example.com/logo.png",
    content_type="image"
  )
])
```

#### discard_recording

**Description**: Prevents saving the current call recording, e.g. when sensitive data is detected.

**Example**:

```python
if conv.state.get("contains_pii"):
  conv.discard_recording()
```

#### generate_external_event

**Description**: Generates a unique external event ID linked to the current conversation. Use this ID to receive webhook data from an external provider through the `/v1/external-events/webhook` endpoint. The webhook payload is then accessible through `conv.generic_external_events`.

**Parameters**:

* send_to_llm (bool, keyword-only, optional) – if `True`, the webhook payload is also sent to the LLM as a system prompt. Default `False`.

**Returns**: str – the generated external event ID (expires after 1 hour).

**Example**:

```python
event_id = conv.generate_external_event(send_to_llm=True)
# Pass event_id to an external provider, which can POST data back via the webhook
```

#### log_api_response

**Description**: Logs an external API response for visibility in Conversation Review → Diagnosis and the analytics pipeline.

**Where logs appear**:

* **Conversation Review**: View API responses in the Diagnosis tab for debugging
* **Analytics pipeline**: API response data is available for reporting and analysis

**Example**:

```python
response = requests.get("https://api.example.com/user")
conv.log_api_response(response)
# Response will appear in Conversation Review → Diagnosis
```

#### send_whatsapp

**Description**: Sends a WhatsApp message through a Twilio subaccount. Both the sender phone number and the message template must be pre-approved by Meta. Once approved, Twilio provides a `content_id` that must be referenced when sending.

> **Note**
>
> WhatsApp messaging requires a configured Twilio integration and is not a native PolyAI channel. Contact your PolyAI representative for setup.

**Parameters**:

* `to_number` (str) – recipient phone number.
* `from_number` (str) – sender phone number (must be WhatsApp-enabled in Twilio).
* `content_id` (str) – alphanumeric ID of the approved WhatsApp message template.
* `content` (str, optional) – text content. Default `""`.
* `retry_count` (int, optional) – number of retries to attempt on failure.

**Example**:

```python
conv.send_whatsapp(
  to_number=conv.caller_number,
  from_number="+441234567890",
  content_id="HX5678efgh",
)
```

For richer templating with variables, use [`send_content_template`](#send_content_template) with `whatsapp=True`.

#### send_content_template

**Description**: Sends a WhatsApp or SMS template message through Twilio's Content API. Requires the content template to be pre-approved in your Twilio account.

> **Note**
>
> WhatsApp messaging requires a configured Twilio integration and is not a native PolyAI channel. Contact your PolyAI representative for setup.

**Parameters**:

* messaging_service_id (str) – Twilio messaging service ID.
* to_number (str) – recipient phone number.
* content_id (str) – alphanumeric ID of the approved message template.
* content (str, optional) – text content. Default `""`.
* whatsapp (bool, optional) – set to `True` to send over WhatsApp. Default `False`.
* content_variables (dict, optional) – variables to pass to the message template.

**Example**:

```python
conv.send_content_template(
  messaging_service_id="MG1234abcd",
  to_number="+441234567890",
  content_id="HX5678efgh",
  whatsapp=True,
  content_variables={"1": "12345", "2": "shipped"}
)
```

#### send_sms

**Description**: Queue a plain-text SMS.

**Example**:

```python
conv.send_sms(to_number=conv.caller_number, from_number="+441234567890", content="Thanks for calling – here's your link: https://…")
```

#### send_sms_template

**Description**: Queue a pre-configured SMS template.

**Example**:

```python
conv.send_sms_template(to_number=conv.caller_number, template="booking_confirmation")
```

#### write_metric

**Description**: Write a custom metric to the analytics pipeline. This is the only way custom metrics are recorded for a conversation – the agent does not write them automatically at runtime, so you must call `write_metric` explicitly from a function whenever you want a metric value captured.

**Parameters**:

* name (str) – metric key, as defined in your project's metrics configuration.
* value (str, int, float, bool, or None) – the metric value. Must match the type defined in the metric spec.
* write_once (bool, optional) – if `True`, the metric can only be written once per conversation. Subsequent calls with the same name are ignored. Default `False`.

**Example**:

```python
conv.write_metric("agent_handoff", 1)
conv.write_metric("call_outcome", "resolved", write_once=True)
```

#### call_handoff

**Description**: Transfer the call to a configured handoff destination.

**Parameters**:

* destination (str) – handoff target key, as defined in your handoff configuration.
* reason (str, optional) – escalation reason. Defaults to the destination name.
* utterance (str, optional) – message for the agent to say before transferring.
* sip_headers (dict\[str, str], optional) – SIP headers to pass through. Merged with any headers configured in the handoff config, with passed headers taking precedence.
* route (str, optional) – phone number or route to override the configured route.

**Example**:

```python
conv.call_handoff(
  destination="BillingQueue",
  reason="policy_violation",
  utterance="Let me transfer you to a specialist who can help."
)
```

**Where it shows up**: In flows using builtin-handoff or using functions; visible in Conversation Review and API.

#### utils

**Description**: Provides helper functions for extracting data, validating entities, and accessing secrets.

**Available utilities**:

* `get_secret(name)` – Retrieve a stored [secret](/advanced/secrets/overview) by name
* `extract_address()` – Extract postal addresses from user input
* `extract_city()` – Extract city references from user input
* `prompt_llm()` – Perform a standalone LLM request
* `validate_entity()` – Validate a value against an entity config (email, phone, date, etc.)

**Note**: Some utilities require activation. If a method raises a `NotImplementedError`, contact your PolyAI representative to enable it for your account.

**Example**:

```python
api_key = conv.utils.get_secret("stripe_api_key")
address = conv.utils.extract_address(country="US")
```

See [Conversation utilities](/agent-studio/tools/classes-and-objects/utilities) for the full list of available helpers and detailed documentation.

#### set_voice

**Description**: Change the TTS voice for the current conversation moving forward.

**Parameters**: voice (TTSVoice) – the voice configuration to use.

**Example**:

```python
from polyai.voice import ElevenLabsVoice
conv.set_voice(ElevenLabsVoice(provider_voice_id="abc123"))
```

#### set_language

**Description**: Change the language for the current conversation moving forward.

**Parameters**: language (str) – ISO 639 language code (e.g., "en-US", "es-ES", "fr-FR").

**Example**:

```python
conv.set_language("es-US")
```

#### set_asr_biasing

**Description**: Dynamically configure ASR keywords and custom biases for improved speech recognition. Biasing persists across turns until cleared.
**Parameters**:

* keywords (list\[str], optional) – list of keywords to bias ASR recognition toward.
* custom_biases (dict\[str, float], optional) – dictionary mapping phrases to bias weights.
  **Example**:

```python
conv.set_asr_biasing(
  keywords=["reservation", "booking", "cancel"],
  custom_biases={"reservation": 3.0, "cancellation": 2.5}
)
```

#### clear_asr_biasing

**Description**: Clear any previously set ASR biasing for future turns.

**Example**:

```python
conv.clear_asr_biasing()
```

#### disable_kb_topics

**Description**: Exclude one or more [FAQs](/agent-studio/knowledge/fa-qs/fa-qs) from RAG retrieval for the rest of the conversation. Useful when an upstream IVR or runtime context means certain topics should not be matched. The list persists across turns until cleared or replaced. See [Disable KB topics](/agent-studio/tools/classes-and-objects/disable-kb-topics) for full details.

**Parameters**:

* topics (list\[str]) – Managed Topic names to disable. Replaces any previously disabled list.

**Example**:

```python
if conv.state.get("ivr_intent") == "billing_handled":
  conv.disable_kb_topics(["refund_policy", "billing_dispute"])
```

#### clear_disabled_kb_topics

**Description**: Re-enable any [FAQs](/agent-studio/knowledge/fa-qs/fa-qs) that were previously disabled with `conv.disable_kb_topics()`.

**Example**:

```python
conv.clear_disabled_kb_topics()
```

#### goto_csat_flow

**Description**: Trigger a transition to the CSAT (Customer Satisfaction) survey flow for voice calls.

**Example**:

```python
if conv.state.get("call_completed"):
  conv.goto_csat_flow()
```

#### set_csat_eligibility

**Description**: Override whether the conversation is eligible for a [CSAT survey](/agent-studio/analytics/surveys). When `eligible=False`, the conversation is excluded from CSAT regardless of call type, percentage rollout, or weekly caps. When `eligible=True` or this method isn't called, normal CSAT logic applies.

**Parameters**:

* `eligible` (bool) – whether the conversation is eligible for CSAT.
* `reason` (str, optional) – reason for the decision (logged for debugging).

**Example**:

```python
if conv.state.get("contains_pii"):
  conv.set_csat_eligibility(False, reason="PII detected")
```

#### set_csat_phone_number

**Description**: Override the phone number used for CSAT SMS surveys. Useful when the real caller number is delivered via a SIP header (e.g., behind an IVR) rather than as the caller ID.

**Parameters**:

* `phone_number` (str) – phone number to send the CSAT SMS to, in [E.164](http://twilio.com/docs/glossary/what-e164) format.

**Example**:

```python
real_caller = conv.sip_headers.get("X-Real-Caller")
if real_caller:
  conv.set_csat_phone_number(real_caller)
```

#### set_csat_survey_entered

**Description**: Mark that the caller entered the CSAT survey flow. This is used internally by the platform's voice CSAT flow for analytics and is not typically called from custom functions.

**Example**:

```python
conv.set_csat_survey_entered()
```

#### send_email

**Description**: Send an email from the function.

This function sends outbound emails during a conversation (e.g., confirmations or notifications). **Email is not a supported inbound channel for PolyAI agents.**

**SMTP configuration**: Emails are sent through a managed delivery service. For custom email delivery requirements, contact your PolyAI representative.

**Delivery considerations**:

* Emails are sent asynchronously after the turn completes
* Delivery failures are logged but do not interrupt the conversation
* For high-volume sending, consider rate limits and reputation management

**Parameters**:

* to (str) – recipient email address.
* body (str) – raw body of the email.
* subject (str) – subject line.

**Example**:

```python
conv.send_email(
  to="customer@example.com",
  body="Thank you for your order!",
  subject="Order Confirmation"
)
```

#### set_response_suggestions

**Description**: Set quick-reply suggestions that appear as clickable options for the user. Only supported on webchat channels.
**Parameters**: suggestions (list\[str]) – list of suggested responses.
**Example**:

```python
conv.set_response_suggestions(["Yes, confirm", "No, cancel", "More options"])
```