Campaigns
API reference for creating campaign drafts, sending or scheduling them with warm-up, and reading their results.
Using an AI agent? Just ask:“Draft a campaign to my Newsletter list and show me before sending.”Connect your agent →
Create a draft#
POST/api/v1/campaigns
Creates a draft. Nothing is sent. The response says whether it's ready (ready_to_send), what's missing (problems), and how many people it would reach (recipients). When that's more than your daily campaign limit allows today, sends_over says how it continues on the following days, e.g. { "days": 3, "today": 1000, "per_day": 1000 }; otherwise it's null. The send response has it too.
Body
namestringrequiredInternal name.fromstringrequiredSender on a verified domain:Acme <[email protected]>.liststring | string[]requiredA list name or id, or up to 50 of them. Someone on several lists gets it once.subjectstringRequired unless the template has one.htmlstringThe email HTML. Your postal address and an unsubscribe link are added automatically.templatestringUse a saved template's content instead of html.preheaderstringPreview text.reply_tostringWhere replies go.
Request
curl https://maillayer.com/api/v1/campaigns \
-H "Authorization: Bearer $MAILLAYER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "November update",
"from": "Acme <[email protected]>",
"list": ["Newsletter", "Customers"],
"subject": "What we shipped in November",
"html": "<h1>Hi {{ first_name | default: \"there\" }}</h1><p>…</p>"
}'Response · 201
{
"id": "c7d2…",
"name": "November update",
"status": "draft",
"subject": "What we shipped in November",
"from": "Acme <[email protected]>",
"lists": ["Newsletter", "Customers"],
"scheduled_at": null,
"sent_at": null,
"ready_to_send": true,
"problems": [],
"recipients": 4812,
"sends_over": null,
…
}Send or schedule#
POST/api/v1/campaigns/:id/send
An empty body sends now. The campaign must be a draft and ready.
Body
scheduled_atstringISO 8601 time at least a minute from now, to schedule instead.review_notestringOnly when asked withreview_required: a new sender's first campaign to 5,000+ people is reviewed before it goes out. Say where the contacts came from and how they agreed to get email. The campaign then showsstatus: "review".warmuptrue | objectSend in growing batches.trueuses the defaults, or setinitial_batch(50),growth_factor(2),interval_hours(24) andmax_batch(10,000).per_hournumberSpread the send evenly at this many emails per hour, 1 to 100,000.120sends one email every 30 seconds. Can't be combined withwarmup.
Request
curl https://maillayer.com/api/v1/campaigns/c7d2…/send \
-H "Authorization: Bearer $MAILLAYER_KEY" \
-H "Content-Type: application/json" \
-d '{ "scheduled_at": "2026-11-18T09:00:00Z", "warmup": true }'This emails real people and can't be undone. Errors: 409
not_draft, 422 not_ready, invalid_schedule or invalid_pace.Get a campaign#
GET/api/v1/campaigns/:id
Status and results:
Response · 200
{
"id": "c7d2…",
"status": "sent",
…,
"warmup": null,
"per_hour": null,
"finishes_at": null,
"stats": {
"recipients": 4812, "sent": 4812, "delivered": 4760,
"opened": 2015, "clicked": 402, "bounced": 52,
"complained": 1, "unsubscribed": 9,
"open_rate": 42.3, "click_rate": 8.4, "bounce_rate": 1.1
},
"dashboard_url": "https://maillayer.com/campaigns/c7d2…"
}List campaigns#
GET/api/v1/campaigns?limit=20
Recent campaigns, newest first, in data. limit goes up to 100. See the Campaigns guide.