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
- Sign in to the dashboard and add a domain (Domains). Add the returned DNS records so the domain verifies.
- Create an API key (API Keys). Copy the key once; it won’t be shown again.
- Create a template (Templates or API
POST /v1/templates) with subject and body. Use {{variable}} for substitution. - Send a test:
POST /v1/emails/sendwithto,from(verified domain), andtemplate+variables. - 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
/v1/emails/sendSend 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)"
}/v1/emails/:idGet 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": "..." } }
]
}/v1/eventsList 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"
}/v1/domainsGET: 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": "..."
}/v1/webhooksRegister 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"]
}/v1/templatesList 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"
}
]
}/v1/templatesCreate 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.