/emails
application/json- body
from*stringSender, "Name user@domain" or bare address; the domain must be verified for the team
to*|array<string>Recipient address or list of up to 50
subject*stringSubject line
1 <= length <= 998html?stringHTML body; at least one of html/text is required
text?stringPlain-text body; at least one of html/text is required
cc?|array<string>Cc address or list
bcc?|array<string>Bcc address or list
reply_to?|array<string>Reply-To address or list
scheduled_at?stringDeliver later: ISO 8601 with offset, or relative like "in 2 hours" (an ISO 8601 datetime with offset (e.g. "2026-09-01T12:00:00Z") or a relative time like "in 5 mins", "in 2 hours", or "in 1 day"); max 30 days ahead
tags?array<>Key/value labels attached to the email for filtering; names starting with millionsend_, in any letter case, are reserved
topic_id?stringTopic id: recipients opted out of the topic are skipped; a send to one address that is a contact also gets that contact's unsubscribe link
uuidattachments?array<>Attachments with base64 content (no remote paths)
headers?Extra message headers (transport headers are rejected)
template?unknownNot supported: any value is a 422; an email takes html and/or text
Email accepted
application/json- response
id*stringuuidwarning?Only on an email the new domain limit holds for later. It is still accepted and goes out on its own.
curl -X POST 'https://api.millionsend.com/emails' \ -H 'Content-Type: application/json' \ -d '{ "from": "string", "to": "string", "subject": "string"}'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "warning": { "code": "new_domain_limit", "message": "string", "sends_after": "string" }}