Inbox: sol-ai@agentmail.to
API key: ~/.openclaw/workspace/secrets/sol-agentmail-api-key.txt
from agentmail import AgentMail
key = open('/Users/amre/.openclaw/workspace/secrets/sol-agentmail-api-key.txt').read().strip()
client = AgentMail(api_key=key)
inbox_id = 'sol-ai@agentmail.to'
result = client.inboxes.messages.list(inbox_id=inbox_id, limit=50)
for m in result.messages:
print(m.message_id, m.thread_id, m.subject, m.labels, m.timestamp, m.from_, m.preview[:100])
Returns ListMessagesResponse — iterate .messages. Never subscript with result[0].
# CRITICAL: reply() takes message_id as first argument, NOT thread_id
reply = client.inboxes.messages.reply(inbox_id, message_id, text="Reply text here")
print(reply.message_id) # confirms it sent
message_id ≠ thread_id. Passing thread_id returns HTTP 404 NotFoundError. Always use the message_id field from the list output. message_id looks like <CAO=qJ6R...@gmail.com> or <010001a0...@email.amazonses.com>; thread_id is a UUID like f61b0be7-84e0-4214-a3be-8e8e4cd9314a.
# messages.get() requires the exact message_id string from the list endpoint
msg = client.inboxes.messages.get(inbox_id, message_id)
print(msg.body)
send = client.inboxes.messages.send(
inbox_id,
to=['recipient@example.com'],
subject='Subject',
text='Body'
)
The API returns messages in reverse-chronological order with a page token. Use pagination to reach older messages:
all_msgs = []
page_token = None
while True:
result = (client.inboxes.messages.list(inbox_id=inbox_id, limit=50, page_token=page_token)
if page_token
else client.inboxes.messages.list(inbox_id=inbox_id, limit=50))
for m in result.messages:
all_msgs.append(m)
if result.next_page_token:
page_token = result.next_page_token
else:
break
Then filter by date or sender:
from datetime import datetime, timezone, timedelta
cutoff = datetime.now(timezone.utc) - timedelta(days=30)
for m in all_msgs:
if m.timestamp >= cutoff:
print(m.from_, m.subject, m.timestamp)
*** heredoc trapNever pass api_key=*** inline in a shell heredoc — Python sees *** as a bare word and fails with SyntaxError. Read the key from file inside the heredoc instead:
# WRONG — SyntaxError: invalid character
python3 << 'PYEOF'
from agentmail import AgentMail
client = AgentMail(api_key=***) # *** is a bare word in Python!
PYEOF
# RIGHT — single-quoted heredoc, read key inside
python3 << 'PYEOF'
from agentmail import AgentMail
key = open('/Users/amre/.openclaw/workspace/secrets/sol-agentmail-api-key.txt').read().strip()
client = AgentMail(api_key=key)
PYEOF
System python (/usr/bin/python3) lacks the agentmail module. Always use the Homebrew python:
# WRONG — wrong python, ModuleNotFoundError
python3 << 'PYEOF'
from agentmail import AgentMail # fails
PYEOF
# RIGHT — explicit Homebrew python
/opt/homebrew/bin/python3 << 'PYEOF'
from agentmail import AgentMail # works
PYEOF
Or add it to PATH at the top of the bash script:
#!/bin/bash
export PATH="/opt/homebrew/bin:$PATH"
If your script writes to a state file, create the directory before the python heredoc runs (bash runs before the heredoc):
SEEN="/Users/amre/.openclaw/workspace/scripts/engagement/state/seen_ids.txt"
mkdir -p "$(dirname "$SEEN")" # creates dir BEFORE python runs
/opt/homebrew/bin/python3 << 'PYEOF'
import os
seen_ids = set()
seen_file = '/Users/amre/.openclaw/workspace/scripts/engagement/state/seen_ids.txt'
if os.path.exists(seen_file):
with open(seen_file) as f:
seen_ids = set(line.strip() for line in f if line.strip())
PYEOF
When a cron is registered with --announce, OpenClaw reads its stdout and delivers to Telegram. Never send Telegram messages directly via curl:
# RIGHT — print to stdout, OpenClaw handles delivery
if [[ ${#new_important[@]} -gt 0 ]]; then
echo "📧 New email — don't reply yet"
for item in "${new_important[@]}"; do
echo "From: ${item['from']}"
done
fi
# Exit 0 — "nothing to say" is not an error, suppresses error noise in logs
exit 0
ok (suppressed) in cron status means the cron ran successfully but printed nothing — that is correct behavior.
thread = client.inboxes.threads.get(inbox_id, thread_id)
for msg in thread.messages:
body = getattr(msg, 'body', getattr(msg, 'text', None))
print(msg.from_, msg.timestamp, msg.subject, body)