YourMail

API reference

Send email

POST/v1/emails

Queue a single email for delivery.

Why use this

This is the heart of YourMail: send a single transactional email — a receipt, a password reset, a welcome message — straight from your app with one API call. There's no mail server to run and no SMTP to configure, and you get back an ID so you can follow whether it was delivered.

For example

A customer checks out in your shop. Your server makes one call to send, and they get their order confirmation — from your own address, tracked end to end.

Example

import { YourMail } from "yourmail";

const yourmail = new YourMail(process.env.YOURMAIL_API_KEY!, {
  baseUrl: process.env.YOURMAIL_BASE_URL!,
});

const { id } = await yourmail.send({
  from: "hello@mail.acme.com",
  to: ["alice@example.com"],
  cc: ["bob@example.com"],
  bcc: ["archive@acme.com"],
  replyTo: "support@acme.com",
  subject: "Welcome to Acme",
  html: "<h1>Hello Alice</h1>",
  text: "Hello Alice",
  attachments: [
    {
      filename: "invoice.pdf",
      content: "<base64-encoded content>",
      contentType: "application/pdf",
    },
  ],
  tags: [{ name: "category", value: "welcome" }],
  idempotencyKey: "welcome-usr_123",
  headers: {
    "List-Unsubscribe": "<mailto:unsub@acme.com>",
    "X-Campaign-ID": "launch-2026",
  },
});

console.log(id);

Request body

All fields are JSON. from, to, subject, and at least one of html / text are required.

fromstring
RequiredA bare address (hello@mail.acme.com) or one with a display name (Acme Support <hello@mail.acme.com>) — the display name is what the recipient's inbox shows. Names containing a comma or dot are quoted for you. Must be on a verified domain you own — the one exception is no-reply@yourmail.dev, the shared sandbox sender, which puts the send in test mode instead. Ownership is checked against the address, never the display name.
tostring | string[]
RequiredOne or more recipient addresses, each with an optional display name. Combined with cc + bcc: max 50 total. In test mode (from: no-reply@yourmail.dev) any recipient other than your signup email or an SES simulator address is silently rewritten to your signup inbox (cc/bcc dropped), not rejected — you may omit to entirely and it defaults to your own inbox.
ccstring | string[]
OptionalCarbon-copy recipients, display names allowed. Counts toward the 50-recipient cap.
bccstring | string[]
OptionalBlind-copy recipients, display names allowed. Counts toward the 50-recipient cap.
replyTostring
OptionalReply-To address, display name allowed. Must be a valid email.
subjectstring
RequiredNon-empty string.
htmlstring
ConditionalHTML body. At least one of html or text is required. Combined html + text must be under 1 MB (UTF-8 bytes).
textstring
ConditionalPlain-text body. At least one of html or text is required.
attachmentsAttachment[]
OptionalMax 20 files. Each file: max 10 MB decoded. Total: max 25 MB decoded. Each item needs filename (string) and content (base64 string); contentType defaults to application/octet-stream.
tagsTag[]
OptionalMax 10 tags. Each tag is { name: string; value: string }. Used for filtering and analytics.
idempotencyKeystring
OptionalIf a send with this key was already queued, the original id is returned without re-sending. Safe to retry on network failures.
headersobject
OptionalCustom mail headers as a string→string object. Max 10 headers; each name max 64 chars; each value max 1 KB; combined total max 4 KB. Reserved headers (From, To, Subject, Content-Type, DKIM-Signature, etc.) are rejected.
bulkboolean
OptionalMarks the send as bulk (newsletters, announcements) rather than transactional. Requires exactly one `to` recipient and no cc/bcc — anything else is a 400. On a live send we inject one-click List-Unsubscribe headers so mailbox providers show an unsubscribe button, unless you set those headers yourself (test-mode sends go to your own inbox, so they get none). Recipients who unsubscribe are blocked from bulk mail only; your transactional email still reaches them. Defaults to false.

Response

On success the API returns HTTP 200 with the new email ID.

// 200 OK
{ "id": "jn7abc123def456" }

Errors

All errors follow the shape { error: { type, message } }. See Errors for the full reference. Common errors for this endpoint:

400validation_error

Missing or invalid field (from, to, subject, html/text)

401authentication_error

API key missing, malformed, or not recognised

403domain_error

Sending domain not verified or not owned by this account

413validation_error

Body exceeds 1 MB, or attachment exceeds 10 MB / 25 MB total

422suppressed_recipient

Recipient is on this account's suppression list

422sandbox_recipient

Test mode only, and rare: a disallowed recipient is normally rewritten to your signup inbox rather than rejected — this fires only when your account has no signup email on file to rewrite to

429rate_limited

Per-second burst exceeded — Retry-After header tells you when to retry

429quota_exceeded

Daily or monthly send quota exhausted