quickstart

Send your first email in three steps: verify a domain, create an API key, send.

1 · add and verify your domain

In the dashboard, add your domain. You get exactly two CNAME records to publish — no MX changes, no SPF edits, nothing vendor-specific. Once DNS propagates, hit verify (we also re-check automatically every 10 minutes).

2 · create an API key

Create a key with sending_access scope for your app. It's shown once — store it as an environment variable.

3 · send

curl -X POST https://app.mailway.prevue.click/api/emails \
  -H "Authorization: Bearer mw_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "You <hello@yourdomain.com>",
    "to": ["someone@example.com"],
    "subject": "hello from mailway",
    "html": "<p>it works!</p>",
    "text": "it works!"
  }'

Node (any fetch-capable runtime):

const res = await fetch("https://app.mailway.prevue.click/api/emails", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.MAILWAY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "You <hello@yourdomain.com>",
    to: ["someone@example.com"],
    subject: "hello from mailway",
    html: "<p>it works!</p>",
  }),
});
const { id } = await res.json(); // poll GET /emails/{id} for the timeline

test without a real inbox

Send to delivered@simulator.mailway.dev, bounced@simulator.mailway.dev or complained@simulator.mailway.dev — delivery, bounce and complaint flows run end to end (including suppressions and webhooks) without touching a real mailbox.

idempotency

Pass an Idempotency-Key header on sends. Same key + same body within 24h returns the original response; same key + different body returns a 409. Safe to retry, safe for agents.

scheduling

{ "scheduled_at": "2026-09-01T09:00:00Z", ... }  // cancel via POST /emails/{id}/cancel