Use case
An email inbox for an AI sales (SDR) agent
A sales agent writes to one person at a time, notices when they answer, and knows the difference between a reply and an out-of-office. It needs an address people can answer, and a sending reputation you don’t mind it using.
An SDR agent researches a lead, writes a first email, follows up if there is no answer, and hands the conversation to a person when the lead is interested. The email part needs three things: an address on your domain, reliable threading so follow-ups land in the same conversation, and a clean way to tell real replies from automatic ones.
#The flow
- Address on your domain. Connect a subdomain such as
mail.acme.comunder custom domains and create the inbox on it. Mail is DKIM-signed with that domain’s own key, so it aligns with DMARC and builds reputation on a subdomain, away from your team’s main domain. - First email.
messages.sendfrom the agent’s inbox, one recipient, written for that person. Use anIdempotency-Keyper lead and step, so a retried job never sends twice. - Replies. Answers are threaded to the original by the standard headers. Listen for
message.receivedon a webhook, or long-pollmessages.waitwithfrom=the lead’s address. - Triage. Skip
ai:auto-reply(out-of-office, ticket acknowledgements) and routeai:needs-humanto a person. Everything else is a real answer for the agent or your team. - Follow-up.
messages.replyon the last message keeps the conversation in one thread in the lead’s mail client. - CRM. Put the CRM id on the contact’s
metadataand update it as the lead moves, so the agent can always look up the current record.
#Send, wait, record
from agentboxd import Agentboxd
mr = Agentboxd()
inbox = mr.inboxes.create(client_id="sdr-emea", display_name="Sam at Acme", domain="mail.acme.com")
sent = mr.messages.send(
inbox["id"],
to="priya@example.com",
subject="Your warehouse move in October",
text="Hi Priya, ...", # one person, one message: no mail merge blasts
idempotency_key="lead-4821-step-1",
)
reply = mr.messages.wait(inbox["id"], since=sent["created_at"], from_="priya@example.com", timeout=60)
if reply and "ai:auto-reply" not in reply["labels"]:
contact = mr.contacts.by_address("priya@example.com")
mr.contacts.update(contact["id"], metadata={"crm_stage": "replied"}, add_labels=["lead"])wait returns within a minute or None; for real follow-up timing (days, not seconds), run the agent from a webhook and a scheduler instead of holding a call open.
#Guardrails that protect your domain
- Every plan has a workspace-wide daily send cap (100 a day on Free), and each inbox has its own daily limit. Both answer
429when reached. - Hard bounces and spam complaints put the address on a suppression list; the next send to it fails with
422 recipient_suppressed. - Workspaces with a bounce rate over 5% or a complaint rate over 0.1% over 7 days are paused (
403 org_suspended). That protects every customer’s deliverability, including yours.
#Build it
- LangChain email tools or OpenAI Agents SDK tools for the agent itself.
- n8n to send from a CRM trigger and wait for the answer.
- Giving an AI agent its own email address: SPF, DKIM, DMARC, bounces and threading, explained.