Marshmellos

API Reference

Implementation guide for the Marshmellos API. Base URL: https://api.marshmellos.com (when live).

For a product overview and capabilities, see the homepage.

Getting started in 5 minutes

  1. Sign in to the dashboard and add a domain (Domains). Add the returned DNS records so the domain verifies.
  2. Create an API key (API Keys). Copy the key once; it won’t be shown again.
  3. Create a template (Templates or API POST /v1/templates) with subject and body. Use {{variable}} for substitution.
  4. Send a test: POST /v1/emails/send with to, from (verified domain), and template + variables.
  5. Check Messages in the dashboard for status and event timeline.

Authentication

Send your API key in the Authorization header as a Bearer token.

Authorization: Bearer mk_live_your_api_key_here

Create and manage API keys in the dashboard under API Keys. Never expose your key in client-side code.

Examples

Send an email with a template (Node.js style):

// POST /api/v1/emails/send
await fetch("https://api.yourdomain.com/api/v1/emails/send", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + process.env.MARSHMELLOWS_API_KEY,
  },
  body: JSON.stringify({
    to: ["[email protected]"],
    from: "[email protected]",
    template: "verify_email",
    variables: { name: "John", link: verificationLink }
  }),
});

cURL:

curl -X POST https://api.yourdomain.com/api/v1/emails/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -d '{"to":["[email protected]"],"from":"[email protected]","template":"welcome","variables":{"first_name":"Alex"}}'

Endpoints

POST/v1/emails/send

Send an email. Requires emails:send scope. Body: to (array), from (string), subject (string, required; use empty string "" when template provides subject), html/text (optional), template (id or slug), variables (object). Returns status and external_id when sent. Idempotency-Key header (e.g. UUID): same key within 24h returns cached response to avoid duplicate sends.

Request body

{
  "to": ["[email protected]"],
  "from": "[email protected]",
  "subject": "Welcome, {{first_name}}",
  "template": "welcome",
  "variables": { "first_name": "Alex" }
}

Response

{
  "id": "clx...",
  "status": "sent",
  "created_at": "2024-01-15T12:00:00Z",
  "external_id": "Provider message ID (when sent)"
}
GET/v1/emails/:id

Get message status and events. Requires emails:read scope. Returns 404 if message not in account.

Response

{
  "id": "clx...",
  "to": ["[email protected]"],
  "from": "[email protected]",
  "subject": "Welcome",
  "status": "sent",
  "created_at": "2024-01-15T12:00:00Z",
  "sent_at": "2024-01-15T12:00:01Z",
  "events": [
    { "type": "queued", "at": "...", "payload": {} },
    { "type": "sent", "at": "...", "payload": { "externalId": "..." } }
  ]
}
GET/v1/events

List message events for the account. Optional query: limit (default 50, max 100), type (sent|delivered|opened|clicked|bounced|complained). Returns next_cursor when more results exist.

Response

{
  "data": [
    {
      "id": "ev_1",
      "type": "delivered",
      "message_id": "clx...",
      "message_subject": "Welcome",
      "at": "2024-01-15T12:00:05Z",
      "payload": {}
    }
  ],
  "next_cursor": "ev_last_id_or_null"
}
GET / POST/v1/domains

GET: List domains for the account (emails:read). POST: Add a domain for verification. Returns DNS records to add.

Request body

{
  "domain": "mail.yourcompany.com"
}

Response

{
  "id": "dom_xyz",
  "domain": "mail.yourcompany.com",
  "status": "pending",
  "spf_record": "v=spf1 include:_spf.example.com ~all",
  "dkim_record": "..."
}
POST/v1/webhooks

Register a webhook endpoint. Payloads include event_id, created, account_id, message_id, data. Secret returned once; verify X-Webhook-Signature (sha256=hex).

Request body

{
  "url": "https://api.yourcompany.com/events",
  "events": ["message.sent", "message.bounced"]
}

Response

{
  "id": "wh_abc",
  "url": "https://api.yourcompany.com/events",
  "secret": "whsec_...",
  "events": ["message.sent", "message.bounced"]
}
GET/v1/templates

List templates for the account. Requires emails:read scope.

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "Welcome",
      "slug": "welcome",
      "subject": "Hello {{name}}",
      "type": "transactional",
      "variables": ["name"],
      "created_at": "2024-01-15T12:00:00Z"
    }
  ]
}
POST/v1/templates

Create a template. Requires emails:read scope. Body: name (required), slug, subject, html_body, text_body, variables (string[]), type (transactional|marketing).

Request body

{
  "name": "Welcome",
  "slug": "welcome",
  "subject": "Hello {{name}}",
  "html_body": "<p>Hi {{name}}</p>",
  "variables": ["name"],
  "type": "transactional"
}

Response

{
  "id": "clx...",
  "name": "Welcome",
  "slug": "welcome",
  "subject": "Hello {{name}}",
  "type": "transactional",
  "variables": ["name"],
  "created_at": "2024-01-15T12:00:00Z"
}

Using Marshmellos for email verification

Use this platform as the sender for your app's signup and verification flows. Create a template (e.g. verify_email) with variables link, name, email. Your app generates a token, builds the verification URL, and calls POST /v1/emails/send with that template. When the user clicks the link, your app validates the token and activates the account. Default templates: verify_email, password-reset, welcome (seed or create in dashboard).

How to send verification emails

For the classic "confirm your email" flow: create a template with variables link, name, and optionally email. Your app generates a verification token and a URL (e.g. https://yourapp.com/verify?token=...), then sends the email via our API with that link in variables.link.

// Your app: generate token, build link, send
const token = await yourAuth.createVerificationToken(userId);
const link = `https://yourapp.com/verify?token=${token}`;

await fetch("/api/v1/emails/send", {
  method: "POST",
  headers: { "Content-Type": "application/json", "Authorization": "Bearer " + API_KEY },
  body: JSON.stringify({
    to: [user.email],
    from: "[email protected]",
    template: "verify_email",
    variables: { name: user.name, link }
  }),
});

When the user clicks the link, your app validates the token and activates the account. We only send the email; you own the token and the verify endpoint.

Replacing Resend with our API

If you currently use Resend, you can switch to Marshmellos with a thin wrapper. Same pattern: to, from, subject, html or template + variables.

// Resend-style helper
async function sendEmail({ to, from, subject, html, template, variables }) {
  const res = await fetch(process.env.MARSHMELLOWS_API_URL + "/api/v1/emails/send", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + process.env.MARSHMELLOWS_API_KEY,
    },
    body: JSON.stringify({
      to: Array.isArray(to) ? to : [to],
      from,
      subject: subject ?? "",
      ...(template ? { template, variables: variables ?? {} } : { html }),
    }),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error ?? "Send failed");
  return { id: data.id, status: data.status };
}

Differences: we require a verified domain for from; we support idempotency via Idempotency-Key header; and we return our message id plus provider id when sent.

All endpoints above are implemented. Rate and size limits apply to send; see the dashboard for your account limits.