How to Send Email with Python and the Robotomail API
Send from Python using the standard library, check HTTP errors and connect incoming replies to a mailbox workflow.
John Joubert
Founder, Robotomail

Table of contents
Python can send email through SMTP or an HTTP API. If your application also needs a mailbox for replies, an email API gives you a direct way to connect sending, receiving and conversation history.
This guide uses Robotomail's HTTP interface and Python's standard library. It keeps credentials on the server, checks HTTP failures and makes the reply workflow explicit. For the broader product, see the email API for agents.
Prepare the mailbox
Create an account, complete email verification and obtain an API key. List the mailboxes using GET /v1/mailboxes, then select the mailbox ID for this workflow. Use a recipient address you control for testing.
Set ROBOTOMAIL_API_KEY, ROBOTOMAIL_MAILBOX_ID and TEST_RECIPIENT in your runtime's environment or secret storage. Never commit the real values to source control or paste them into model prompts.
On Free, set TEST_RECIPIENT to your verified signup email address. Free includes one mailbox, 10 sends and 10 receives per calendar month with that address only. Upgrade to contact other recipients or use a custom domain.
Send a plain-text message
This example requires Python 3 and uses no third-party package:
import json
import os
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import Request, urlopen
key = os.environ['ROBOTOMAIL_API_KEY']
mailbox_id = os.environ['ROBOTOMAIL_MAILBOX_ID']
recipient = os.environ['TEST_RECIPIENT']
payload = {
'to': [recipient],
'subject': 'Hello from Python',
'bodyText': 'This is a controlled test. Reply here to continue the conversation.',
}
request = Request(
'https://api.robotomail.com/v1/mailboxes/'
+ quote(mailbox_id, safe='') + '/messages',
data=json.dumps(payload).encode('utf-8'),
headers={
'Authorization': f'Bearer {key}',
'Content-Type': 'application/json',
},
method='POST',
)
try:
with urlopen(request, timeout=30) as response:
message = json.load(response)['message']
print('Accepted message ID:', message['id'])
except HTTPError as error:
raise SystemExit(f'Send rejected with HTTP {error.code}') from error
except URLError as error:
raise SystemExit('Network failure; reconcile the result before retrying') from error
The body uses bodyText. For an HTML alternative, add bodyHtml while retaining a complete text version. Escape any untrusted values inserted into HTML. Do not assume the service infers the sender mailbox from a From field; the mailbox ID in the path defines the operation's context.
Add HTML or an attachment
An HTML alternative belongs in bodyHtml. Keep essential information, including links and next steps, available in plain text for clients that do not render the HTML as expected.
For attachments, upload the file through the documented attachment endpoint first. Then include the returned IDs in an attachments array in the message body. Uploads require a full-access key; account storage and file limits apply. The attachment tutorial explains the complete flow.
Receive and reply from the Python application
Choose a transport the runtime can operate: a webhook endpoint, a persistent SSE connection or polling. A webhook should verify the raw-body HMAC before parsing the JSON. Store a processed-message or event record to avoid repeating business actions when notifications are retried.
Fetch the message and relevant thread, pass only authorized context to the agent, then validate the proposed response. To send a threaded reply, include the original email's RFC Message-ID in inReplyTo. Keep that value distinct from the database ID used for message retrieval.
Your Python application owns task state, model calls, recipient checks and human handoff. Robotomail provides the mailbox and email interface. Follow receive and reply for the exact event and request schemas.
Where SMTP fits
Python's smtplib handles SMTP submission, while the email package builds MIME messages. An SMTP connection needs the provider's host, port, authentication and transport-security settings. Use authenticated TLS according to that provider's documentation and a provider-supported credential.
SMTP submission alone does not implement mailbox provisioning, incoming-message processing or an agent's task state. A successful SMTP acceptance also does not prove inbox placement. Robotomail's documented send integration uses HTTPS and an API key; do not use the API key as an SMTP password.
Handle failures before scaling
Distinguish an HTTP rejection from an uncertain result after a connection failure. Do not blindly repeat an accepted send. Record the business event and message ID, reconcile the outcome and make your application's notification logic idempotent.
Complete verification before sending, monitor quota responses and respect rate-limit guidance. Watch delivery and bounce events; a delivered message was accepted by the recipient server, not necessarily read. See email delivery.
Use a dedicated mailbox for each workflow that needs a distinct identity, keep credentials restricted and add approval for sensitive agent actions. The Python SDK reference is another integration option once the basic HTTP flow is clear.
Give your AI agent a real email address
Create a mailbox, connect your agent and test a conversation. Send, receive and retrieve the thread through one API.
Related posts

Send Email from an AI Agent (Python + Node)
Send email from your AI agent in under five minutes. Code examples in Python and Node.js for basic sends, HTML email, threading, and a complete agent loop.
Read post
10 Email Automation Best Practices for 2026
Master our top 10 email automation best practices for AI agents. Learn about DKIM, webhooks, threading, and more for secure, reliable agent-native email.
Read post
Inbound Package Meaning: From Warehouse Box to API Payload
Confused by the 'inbound package meaning'? Learn the critical difference between the logistics term and the API payload concept AI developers must know.
Read post