Send your first campaign
Connect an agent, add a domain, import a list, write, test, schedule and watch a real send.
In this tutorial, we send a product update to a list of people who signed up for it. We connect an agent, add a sending domain, import the list, write the email, send a test, schedule the send, and watch the results come in.
It takes about 30 minutes with a real domain, most of it waiting for DNS. To try every step first with no domain
and no real mail, create a sandbox workspace with pneumail workspaces create --sandbox. A sandbox approves sends
automatically and sends everything to the Amazon SES mailbox simulator.
You need three things:
- A Pneumail workspace. Sign in at
app.pneumail.comwith your work email. - A domain where you can add DNS records. We use
news.example.com. - A CSV of people who opted in, with
emailandconsented_atcolumns. We useresidents.csv.
1. Connect your agent
Paste this into Claude Code, Codex, Cursor, or any agent with a terminal:
Set up Pneumail for me. Follow https://pneumail.com/install
The agent installs the CLI and starts a login. You approve it in your browser, or on your phone with the code the
agent shows you. Then the agent adds the MCP server to its config and runs pneumail whoami:
$ pneumail whoami
workspace Front Door
you david@backpackops.com (admin)
key cli-macbook (created just now)
The key can draft, import, and schedule. It cannot approve a send, and it cannot remove anyone from a suppression
list. Every command takes --json and returns a stable error code with a next step, so an agent can act on the
result without parsing text.
2. Add a sending domain
$ pneumail domains add news.example.com
Pneumail prints the DNS records to add:
Add these records at your DNS provider:
CNAME abc123._domainkey.news.example.com abc123.dkim.amazonses.com
CNAME def456._domainkey.news.example.com def456.dkim.amazonses.com
CNAME ghi789._domainkey.news.example.com ghi789.dkim.amazonses.com
MX bounce.news.example.com 10 feedback-smtp.us-east-2.amazonses.com
TXT bounce.news.example.com "v=spf1 include:amazonses.com ~all"
TXT news.example.com "google-site-verification=Xy7..." (Google Postmaster Tools)
DMARC example.com already publishes p=reject. news.example.com inherits it. No record needed.
If your DNS is on Cloudflare, add --apply-dns and Pneumail adds the records with your Cloudflare token.
Now wait for the records to work. DKIM usually verifies within an hour and can take up to 72 hours:
$ pneumail domains verify news.example.com --wait
DKIM pass
SPF pass, aligned through bounce.news.example.com
DMARC pass (inherited p=reject from example.com)
ready news.example.com can send. Warmup allows 2,000 emails today.
A new domain starts at 2,000 emails a day. The limit doubles after each day the domain sends with few bounces.
3. Create a publication
A publication is one stream of mail with its own sender, its own subscribers, and its own unsubscribe list. We create one for product updates:
$ pneumail publications create "Front Door updates" \
--from "Front Door <updates@news.example.com>" \
--reply-to david@backpackops.com \
--address "1234 Example St, Seattle, WA 98101"
created front-door-updates
CAN-SPAM requires a postal address in commercial email, so it goes in every footer.
4. Import your contacts
$ pneumail contacts import residents.csv \
--publication front-door-updates \
--consent form --consent-url https://localcarehub.org/updates
Pneumail checks every address before it adds anything:
rows 4,812
added 4,630
duplicates 0
to review 141 (112 likely typos like gmial.com, 29 role addresses like info@)
rejected 41 (disposable domains)
suppressed 0
Each contact keeps its consent record: how the person agreed, the page, the time from consented_at, and a
fingerprint of the file. A row without consented_at waits for review. Pneumail never changes an address on its
own. Open the review link to accept or reject each suggested fix.
5. Write the email
Ask your agent to draft it, or open the composer:
$ pneumail campaigns create --publication front-door-updates --from-markdown update.md \
--subject "You can now renew SNAP from your phone" \
--preview "Two minutes, no office visit."
created cmp_7Hk2 https://app.pneumail.com/c/cmp_7Hk2
In the composer, type / to add a block: image, button, columns, divider, or quote. The right side shows the email
on a phone, filled in for a sample contact. The size gauge under it reads 38 KB of 102 KB. Gmail cuts off emails
over 102 KB.
6. Send yourself a test
$ pneumail campaigns test cmp_7Hk2 --to david@backpackops.com
sent 1 test email
Tests go only to workspace members and your seed list. Open the test on your phone. Check that the images load, the button works, and the unsubscribe link opens a one-click page.
7. Schedule the send
$ pneumail campaigns schedule cmp_7Hk2 --at "2026-11-03 09:00 America/Los_Angeles"
checks subject, preview, unsubscribe, postal address, size, links: pass
audience everyone in front-door-updates, 4,630 today
status waiting for approval https://app.pneumail.com/c/cmp_7Hk2/approve
Pneumail emails every admin that an approval is waiting. An admin opens the link, sees the email, the audience and where those contacts came from, and clicks Approve. The agent waits for it:
$ pneumail campaigns wait cmp_7Hk2 --until scheduled
scheduled 2026-11-03 09:00 PST
The audience is counted again when the send starts. If it has grown or shrunk by more than 10%, the send waits for a new approval.
8. Watch the results
$ pneumail campaigns stats cmp_7Hk2 --watch
first batch 463 random new contacts, held 30 minutes: 0.4% bounced, sending the rest
day 1 2,000 sent (warmup limit)
day 2 2,630 sent, done
sent 4,630 delivered 4,588 bounced 21 pending 21 unsubscribed 6 complained 1
clicked 612 people (88 bot clicks removed)
opened 838 people (1,102 automatic opens from Apple Mail and scanners not counted)
gmail not enough Gmail volume yet for Google Postmaster to report
Contacts this workspace has never emailed get a test first: a random 10% goes out, and the rest waits 30 minutes. The warmup limit spreads this send over two days. If bounces pass 2% or complaints pass 0.1%, the send pauses, and every admin gets an email saying which limit it hit. Nothing more goes out until an admin resumes it.
You sent your first campaign.
Next steps
The full docs are at docs.pneumail.com. Agents can read them through docs.pneumail.com/llms.txt.
- To add people from your own signup form with double opt-in, read “How to collect signups”. Done when a form
post creates a pending contact, the confirmation email arrives, and the click adds the contact with a
double_opt_inconsent record. - To send a welcome sequence to everyone who signs up, read “How to send a welcome sequence”. Done when a new contact receives three emails on the schedule and the journey view shows each step.
- To turn each changelog post into a draft campaign, read “How to draft campaigns from a changelog”. Done when publishing a post produces a draft with the post’s title, body and link.
- To learn what Pneumail does to keep your mail out of spam folders, read “About deliverability”.
- To look up a command, an endpoint, or an MCP tool, use the reference section.