# The Foundation: Sending HTML & Text

As a quick reminder from our `Messages` documentation, it’s a critical best practice to always provide both an `html` and a `text` version of your email. This ensures readability on all email clients and significantly improves deliverability.

```
# Always provide both html and text when possible
client.inboxes.messages.send(
    inbox_id="outreach@agentmail.to",
    to=["potential-customer@example.com"],
    subject="Following up",
    text="Hi Jane,\n\nThis is a plain-text version of our email.",
    html="<p>Hi Jane,</p><p>This is a <strong>rich HTML</strong> version of our email.</p>",
    labels=["outreach-campaign"]
)
```

## The Conversational Loop

A common task for an agent is to check for replies in an `Inbox` and then respond to them. While using `Webhooks` is the most efficient method for this, you can also build a simple polling mechanism.

Here’s the step-by-step logic for a polling-based conversational agent.

### 1\. Find a Thread that Needs a Reply

First, you need to identify which conversations have new messages that your agent hasn’t responded to. A great way to manage this is with `Labels`. You can list `Threads` in a specific `Inbox` that have an `unreplied` `Label`.

```python
# Find all threads in this inbox that are marked as unreplied
threadsRes = client.threads.list(
    labels=["unreplied"]
)
if threadsRes.count == 0:
    print("No threads need a reply.")
else:
    # Let's work on the first unreplied thread
    thread_to_reply_to = threadsRes.thread[0]
```

### 2\. Get the Last Message ID from the Thread

To reply to a conversation, you need to reply to the _most recent message_ in the `Thread`. You can get a specific `Thread` by its ID, which will contain a list of all its `Messages`. You’ll then grab the ID of the last `Message` in that list.

```python
# Get the full thread object to access its messages
thread_details = client.threads.get(thread_to_reply_to.thread_id)

# The last message in the list is the one we want to reply to
last_message = thread_details.messages[-1]
message_id_to_reply_to = last_message.message_id
```

Use `last_message.extracted_text` (or `extracted_html`) when you need just the new reply content, without quoted history.

### 3\. Send the Reply and Update Labels

Now that you have the `message_id` to reply to, you can send your agent’s response. It’s also a best practice to update the `Labels` on the original `Message` at the same time, removing the `unreplied` `Label` and adding a `replied` `Label` to prevent the agent from replying to the same message twice.

```python
# Send the reply
client.inboxes.messages.reply(
    inbox_id="support@agentmail.to",
    message_id=message_id_to_reply_to,
    text="This is our agent's helpful reply!"
)

# Update the labels on the original message
client.inboxes.messages.update(
    inbox_id="support@agentmail.to",
    message_id=message_id_to_reply_to,
    add_labels=["replied"],
    remove_labels=["unreplied"]
)
```

##### Real-Time Processing with Webhooks

For production applications, polling is inefficient. The best way to handle incoming replies is to use `Webhooks`. This allows AgentMail to notify your agent instantly when a new `Message` arrives, so you can reply in real-time.

## Scheduling Emails

Instead of sending immediately, you can schedule emails for a future time—perfect for delivering messages during business hours or spacing out outreach.

Create a `Draft` with the `send_at` field and AgentMail handles the rest. The email is automatically sent at the specified time.

```python
from datetime import datetime, timedelta

# Schedule for tomorrow at 9 AM UTC
send_time = (datetime.utcnow() + timedelta(days=1)).replace(hour=9, minute=0, second=0)

client.inboxes.drafts.create(
    inbox_id="outreach@agentmail.to",
    to=["prospect@example.com"],
    subject="Quick question about your workflow",
    text="Hi, I noticed you're using...",
    html="<p>Hi, I noticed you're using...</p>",
    send_at=send_time.isoformat() + "Z"
)
```

For more details on scheduled sending—including how to cancel, reschedule, list scheduled drafts, and build conditional follow-up workflows—see the [**Drafts**](/content/docs/drafts/index.html) page.
