Cold email for AI agents
How an AI agent can run cold email safely: what it can do over MCP, REST tools and the CLI, what stays with a person, and how mail it reads is fenced.
An AI agent can run most of outbound: search contacts, build segments, draft campaigns, triage replies and draft answers. Putting mail on the wire stays behind explicit permissions. In AY Inbox dashboard actions are reachable with an API key behind a scope (the few that must stay with a signed-in person each have a recorded reason), and no tool on the MCP or REST tool surfaces sends a message itself.
An AI agent here means a language model that calls tools in a loop: it reads a result, decides the next call, and keeps going until the task is done. The question for cold email is not whether a model can write an email (it can) but which of its calls are allowed to reach a real person's inbox.
Three doors, one registry
AY Inbox exposes one tool registry through three surfaces. Each tool checks its own scope, and the member behind the credential must also hold the matching workspace permission, so a tool behaves the same whichever door it came through.
| Door | Address | Use it when |
|---|---|---|
| MCP server | https://api.ayinbox.com/v1/mcp | Your agent speaks the Model Context Protocol: Claude Code, Claude Desktop, Cursor |
| REST agent tools | GET /v1/ai/tools and POST /v1/ai/tools/{name}/call | A function-calling agent over plain HTTP: OpenAI-compatible, Hermes, LangChain, a script |
ayinbox CLI | ayinbox tool list, ayinbox tool call, plus typed commands | An agent that works in a terminal, or CI |
The list a credential sees is already filtered: tools/list on MCP and GET /ai/tools return only the tools that credential may call. A credential with read scopes sees read tools; add write scopes and the write tools appear. The dashboard assistant runs the same registry as the signed-in member, and rate limits apply per key (or per user for OAuth tokens) exactly as on the rest of the API.
What an agent can do
The catalog is large, so it is grouped into toolsets a client can narrow to with ?toolsets=: inbox, campaigns, contacts, leads, crm, mailboxes, workspace, automation, forms, developer, research and analytics. A small model does best started on inbox alone. The filter only shortens the list; every call is gated the same way.
| Family | Examples | Scope it needs |
|---|---|---|
| Read | search_contacts, list_campaigns, get_campaign_stats, list_threads, get_thread, list_mailboxes, list_placement_tests | The matching READ_* scope |
| Write | create_campaign_draft, add_campaign_step, create_segment, update_contact_fields, add_suppressions, CRM deals and tasks | The matching WRITE_* scope |
| Draft | generate_reply_draft (the AI writes a reply, charged to credits, never sent), draft_reply (checks text you wrote against the campaign brain, no model) | READ_UNIBOX plus AI_DRAFT for the first, READ_UNIBOX for the second |
| Leads | find_leads (a Looot lead job with a max_cost_usd of 0.05 to 50), verify_email | INTEGRATIONS and BULK_CONTACTS for the first, WRITE_CONTACTS for the second |
| Booking prep | get_availability, offer_meeting_times (prepares a block of times, sends nothing) | READ_CONTACTS (plus READ_UNIBOX with a thread) for the first, WRITE_UNIBOX for the second |
The full table, with every tool and its scope, is on the MCP server page.
What stays with a person
Some actions are not tools at all, whatever scope the credential holds:
- Sending.
send_reply,compose_email,run_placement_testandrun_placement_batchare never listed or callable on MCP or the REST tools. - Booking a meeting. The scheduling provider mails an invitation to a real person, so
book_meetingexists only in the dashboard assistant, where it always asks. Code books throughPOST /booking/offers/:id/bookwith its own key. - Lifting a stop or opening a lasting way out.
remove_suppression(it can undo an unsubscribe or a complaint), creating or updating a webhook, and rotating a webhook secret stay with the assistant, which asks a person each time. - What the AI reads as instructions. No tool changes the workspace's product description, ideal customer notes, voice or playbooks, because every AI writing surface reads them.
- Consent and first emails. No tool records a contact's prior consent or approves a first email in a review queue.
- Governance. Team, invitations, roles, workspace settings writes and billing are not tools.
Beyond the tools, some REST routes refuse API keys altogether and answer only a signed-in person. Each has a recorded reason, for example: a person has to consent in a browser, the route changes how someone signs in or who can get in, it moves money, it exports or destroys the whole workspace (owner only), or it is one person's own screen. Connecting a mailbox is one of them, because it needs OAuth consent or a credential form in a browser.
Starting mail needs SEND_CAMPAIGNS
SEND_CAMPAIGNS is separate from WRITE_CAMPAIGNS on purpose: editing a draft and starting something that transmits are different blast radii. On the tool surfaces it gates:
set_campaign_status, which starts or stops a campaignadd_segment_to_campaignandset_campaign_segmentsinto a campaign that is already sending, and widening a segment that feeds oneset_automation_enabledwithenabled: true, and enabling auto-reply for the workspace or a campaignfind_leadswhen it names a campaign that is sending
An MCP client that registered itself over OAuth is never granted SEND_CAMPAIGNS (nor API_KEYS or WRITE_WORKSPACE). So the usual pattern is: the agent builds a draft campaign and its audience, and a member starts it. One subtlety is worth knowing: once a segment is linked to a sending campaign, a contact that comes to match it through an ordinary contact write is enrolled and mailed. Linking the segment is the decision that needs SEND_CAMPAIGNS.
WRITE_UNIBOX is the other scope to watch. No tool sends with it, but over plain REST it can send through POST /unibox/reply. Give a model a key without it unless the model's code is meant to send.
Mail is untrusted input
Anyone who can email the workspace, fill in a form or appear in an imported list writes text your agent reads. Two defences work together.
- Fenced results. Every tool result starts with one line saying everything between the markers is data, then the result between
<<<UNTRUSTED_CONTENT>>>and<<<END_UNTRUSTED_CONTENT>>>. Markers inside the data are removed, so it cannot close the fence early. The only unfenced result isload_skill, your workspace's own playbook. - A key that cannot do harm. The Agent: read and draft preset (
84574239) holds every read scope plusAI_DRAFT. A steered model on that key can read and draft, and it holds no scope that writes or sends.
The fence lowers the odds that injected text steers the model. The scopes limit what a steered model can do. Rely on both, not on either alone. See Permissions for every scope and preset.
Retries without double effects
Agents retry, and a retried write should not happen twice. Every write tool's schema carries an optional idempotency_key: a retry with the same key and arguments returns the first result for 24 hours instead of running again. The key is bound to the credential that used it. A key reused with different arguments answers idempotency_key_reused, and a retry while the first call is still running answers tool_call_in_progress.
generate_reply_draft requires its key, and a key pays for one draft, so a new draft takes a new key. find_leads takes the key in its own arguments too: a retry with the same key returns the job it already started instead of starting and paying for a second one, while a retry without one starts a second job.
Where to start
- Agent quickstart: a scoped key or OAuth sign-in, triage, a draft and a safe approval, in order.
- Running outbound from Claude Code: the MCP server and the CLI from a terminal session.
- OpenAI agents: wiring the REST tools into an OpenAI-style agent.
- CLI and Agent tools (REST) for the full references.
Frequently asked questions
Can an AI agent send cold email through AY Inbox?
No tool on the MCP server or the REST tool endpoints sends a message. An agent can start a campaign, which then sends, only with the SEND_CAMPAIGNS scope, and an MCP client that registered itself over OAuth is never granted it. Without that scope, a person or your own code starts the mail.
Does AY Inbox have an MCP server?
Yes. It is at https://api.ayinbox.com/v1/mcp over streamable HTTP, and a self-hosted instance serves the same path on its own API host. Clients connect with an OAuth sign-in in the browser or with a scoped API key.
Should an agent use MCP, the REST tools or the CLI?
Whichever it already speaks. All three expose the same AY Inbox tool registry with the same scope checks: MCP for Claude Code, Claude Desktop or Cursor, GET /v1/ai/tools for a function-calling agent over HTTP, and the ayinbox CLI for an agent working in a terminal or CI.
How does AY Inbox stop an email from giving instructions to my agent?
Every tool result is fenced: one line says the content is data, then the result sits between <<<UNTRUSTED_CONTENT>>> and <<<END_UNTRUSTED_CONTENT>>> markers, and markers inside the data are removed. The fence lowers the odds that steering works; the scope rules limit what a steered model could do.
What happens when an agent retries a tool call?
With the same idempotency_key and arguments, AY Inbox returns the first result for 24 hours instead of running the write again. A key reused with different arguments answers idempotency_key_reused, and a retry while the first call still runs answers tool_call_in_progress.
Following up until a meeting is booked
How to follow up after a prospect asks for a call and goes quiet: how many touches, how far apart, fresh times in each, and the signals that end the chase.
Running outbound from Claude Code
Run cold email outreach from Claude Code with the AY Inbox MCP server or CLI: import leads, draft and check a campaign, triage replies and draft answers.