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.

Claude Code can run your outbound through AY Inbox in two ways: as an MCP server added with one command, or through the ayinbox CLI with the repository's ayinbox-cli skill. Claude can then import a list, draft a campaign, run preflight, read replies and draft answers, while starting a campaign or sending a reply stays a step you approve.

Claude Code is Anthropic's coding agent for the terminal. It runs shell commands and calls MCP tools, which is all AY Inbox needs: the MCP server gives it typed tools, and the CLI reaches every REST endpoint through its commands or ayinbox api.

Connect

MCP with OAuth

claude mcp add --transport http ayinbox https://api.ayinbox.com/v1/mcp

Run /mcp inside Claude Code and pick Authenticate. A browser opens, you sign in, approve the scopes, and Claude holds a token it refreshes itself. The scopes it gets are the ones it asked for that your role covers. SEND_CAMPAIGNS is never one of them, so this connection can build and read but never starts mail.

To keep a smaller model focused, narrow the tool list: https://api.ayinbox.com/v1/mcp?toolsets=inbox,campaigns,contacts. The filter is tested with API keys; an OAuth client that ties its token to the exact server URL may not accept it. Prefer a static key? Add --header "Authorization: Bearer aybox_..." and see MCP server for scoping it.

The CLI

curl -fsSL https://github.com/walidboulanouar/ayinbox/releases/latest/download/cli.sh | sh
ayinbox auth login

The installer needs no root and verifies the download against the published checksum. ayinbox auth login shows a code and a URL; you approve in the browser, and the approval creates one API key named for your machine. That step needs a person at a browser, so run it yourself, or set AYINBOX_TOKEN to a key in the environment.

Narrow what the key can do at sign-in. For example, a key that can build campaigns and draft replies but not start or send:

ayinbox auth login --scopes read_campaigns,write_campaigns,read_contacts,write_contacts,read_unibox,read_emails,read_analytics,ai_draft

Give it the playbook

The repository ships an ayinbox-cli skill under skills/: the command map, how to authenticate, which commands send, and the sending limits. Install it the way your agent expects, for example cp -r skills/ayinbox-cli ~/.claude/skills/.

The skill tells Claude to always pass --json. The CLI prints tables on a terminal and JSON when piped, and asking explicitly means the shape never depends on how Claude invoked it. Lists come back as {"data": [...], "pagination": {...}}, and --all walks every page. Failures print a machine-readable code and request_id, and exit code 4 always means the credential: missing, rejected or short a scope.

A first session, step by step

Ask Claude for the outcome ("set up a three-step campaign for the leads in leads.csv") and it will work through roughly this:

  1. Look around. ayinbox status --json shows mailboxes needing attention, what is sending and what is unread. ayinbox mailbox list --json lists the senders.
  2. Create the campaign with its steps. ayinbox campaign create --input @campaign.json --idempotency-key q4-outbound-1. A steps array in the body is connected in order at creation, and each follow-up waits its own wait_after days after the previous step (the first step defaults to 0, follow-ups to 3). A follow-up with no subject of its own is sent as a reply in the same thread.
  3. Pick the senders. ayinbox campaign set-senders CAMPAIGN_ID --input '{"senders":[{"email_account_id":"...","weight":1}]}'.
  4. Import the contacts. Claude turns the CSV into a JSON array and runs ayinbox contact create --input @contacts.json --idempotency-key leads-q4-1. POST /contacts takes an array, and each contact's campaigns field adds it to the new campaign. A large file with column mapping is better as a dashboard import.
  5. Check it. ayinbox campaign preflight CAMPAIGN_ID --json runs every pre-send check without sending: missing senders, an empty audience, broken tracking.
  6. Read the plan. ayinbox campaign plan CAMPAIGN_ID --json shows how many emails go out today and every limit that lowered the number.
  7. You start it. ayinbox campaign start CAMPAIGN_ID in your own terminal, where it asks before sending, or Start in the dashboard.

Over MCP the same flow uses create_campaign_draft, add_campaign_step, set_campaign_senders, add_contact, a segment linked with set_campaign_segments while the campaign is still a draft, and get_campaign to check the result. The start is yours in the dashboard.

Working replies

  1. ayinbox inbox list --unseen --json lists new mail; ayinbox inbox thread --thread-id THREAD_ID --json reads one conversation.
  2. ayinbox inbox agent-drafts --json lists the replies the inbox agent drafted and is holding for review. Each carries grounding.flags: links, figures or phrases the campaign brain does not back.
  3. Over MCP, generate_reply_draft has the AI write a reply for one thread (it needs AI_DRAFT, charges AI credits and never sends), and draft_reply checks text Claude wrote against the brain without calling a model.
  4. Approving sends. ayinbox inbox approve-draft DRAFT_ID asks first, and a flagged draft answers draft_flagged until you read the flags and pass --acknowledge-flags. A flag is a reason to edit, not noise. ayinbox inbox discard-draft DRAFT_ID drops one.

Booking from replies

When a prospect asks for a call, booking offers times from your Cal.com or Calendly:

  1. ayinbox booking availability --thread THREAD_ID --json reads open times in the prospect's timezone.
  2. ayinbox booking offer-create --thread THREAD_ID prepares a block of times and sends nothing.
  3. The block goes into a reply with ayinbox inbox reply, which asks before sending. The offer starts only when that reply is sent.
  4. When the prospect picks, ayinbox booking book OFFER_ID --slot SLOT_ID books it. The provider invites the contact, so it asks like any send. 409 booking_new_reply means a reply on the offer has not been read yet.

Over MCP, get_availability and offer_meeting_times work the same way. Booking itself is not an MCP tool.

Guardrails that stay on

  • --yes only for a send you asked for. campaign start, campaign test, placement test, placement batch-start, mailbox send, inbox reply, inbox compose, inbox approve-draft and booking book ask first and refuse with no terminal. --yes is the only way past, which makes it the flag to look for when you review what Claude ran.
  • Caps stay where they are. The default is 50 campaign emails per mailbox per day with 600 seconds between sends, and a fresh mailbox is better started around 10 to 20. Read campaign plan before touching a cap.
  • No SEND_CAMPAIGNS over OAuth. An MCP client that registered itself never gets it, so it cannot start a campaign, link an audience to a sending one, or turn on an automation.
  • Text is data. Replies, form answers and contact fields are written by people outside your workspace, and tool results arrive fenced as data. A model that reads mail belongs on a key from the Agent: read and draft preset.

Watching live

ayinbox events tail --json --intent EMAIL --count 20

This streams the same live events the dashboard runs on, as JSON, and stops after 20. --intent matches the event type as a substring, so EMAIL takes EMAIL_SENT, EMAIL_OPENED and the rest. It needs a key with REALTIME_SUBSCRIBE. It is the quickest way to confirm a send went out or a reply arrived.

For the scope rules behind all of this, see Cold email for AI agents. For a credential-by-credential walkthrough, see the Agent quickstart, and for every command, the CLI reference.

Frequently asked questions

Can Claude Code send my cold emails on its own?

Not over MCP with an OAuth sign-in: no MCP tool sends mail, and that connection is never granted SEND_CAMPAIGNS, so it cannot start a campaign. Through the CLI, every command that sends asks first and refuses when there is no terminal to ask on, unless --yes is passed.

Should I connect Claude Code through MCP or the ayinbox CLI?

MCP is one command and an OAuth sign-in, and gives Claude typed tools, none of which sends a message. The CLI reaches every endpoint, including starting a campaign and approving a draft, so it suits a person and Claude working in the same terminal.

How do I sign Claude Code in to AY Inbox without a browser?

Set AYINBOX_TOKEN to an API key in the environment. It overrides the CLI's stored sign-in and is never written to disk. For MCP, add the server with an Authorization: Bearer header instead of the OAuth flow.

Does this work with a self-hosted AY Inbox instance?

Yes. Sign the CLI in with ayinbox auth login --hostname and your instance's host, and point MCP at /v1/mcp on your own API host. The hosted service and a self-host run the same code.

On this page