Skip to content

Documentation

Everything you need to integrate and extend Mailtr.

Getting Started

Introduction

Mailtr is a visual email builder that generates clean, responsive HTML compatible with all major email clients. Whether you are a marketer building campaigns or a developer integrating our editor into your SaaS, this documentation will guide you through the process.

New here? Follow the Quick Start to go from sign-up to a sent email in a few minutes, then dip into the API Reference when you are ready to send from code.

Installation

There is nothing to install. Mailtr runs in your browser — create a free account and the editor, template library and dashboard are ready to use.

Sending from your own code needs no SDK either. The HTTP API works with any HTTP client, and the SMTP relay works with any mail library — nodemailer, Python’s smtplib, or your framework’s mailer.

  • Editor: any modern desktop browser.
  • HTTP API: an API key and the base URL https://api.mailtr.co/api/v1.
  • SMTP relay: SMTP credentials from Dashboard → SMTP & API.

Quick Start

  1. Sign up or log in to your workspace.
  2. Start from a design in the Marketplace or from a blank canvas.
  3. Build your email in the drag & drop editor — sections, rows and blocks.
  4. Press Publish on the template’s card to make it the live version.
  5. Export the HTML, or generate an API key in Dashboard → SMTP & API and send it with POST /api/v1/emails/send and its templateId.

Core Concepts

Drag & Drop Editor

The editor is built on a block-based system. Every element in the email (text, image, button, spacer) is a "Block". Blocks are stacked vertically within "Sections" or "Columns".

  • Sections: Full-width containers that hold rows.
  • Rows: Horizontal containers that can be split into columns (1-4).
  • Blocks: The actual content elements.

Using Templates

Templates are reusable designs you build in the editor. Start from one in our marketplace or save your own, then reuse it across sends.

To send a template programmatically, publish it and pass its templateId to the API — see Sending with a template.

Exporting HTML

Our renderer ensures high compatibility. When you export, we:

  • Inline all CSS styles automatically.
  • Convert modern layout properties (Flexbox/Grid) into ghost tables for Outlook.
  • Compress images and optimize assets.

API Reference

Send transactional email over HTTP or SMTP. All requests are made against the base URL https://api.mailtr.co/api/v1. The programmatic sending feature must be enabled on your account.

Authentication

The HTTP API authenticates with an API key. Generate one from Dashboard → SMTP & API. Keys look like mailtr_live_… and are shown only once at creation. Send it as a Bearer token:

HTTP header
Authorization: Bearer mailtr_live_xxxxxxxxxxxxxxxxxxxxxxxx

Keep your key secret. API keys grant sending access to your account. Store them in server-side environment variables — never embed a key in client-side code. Revoke and rotate keys from the dashboard if one is exposed.

Endpoints

Send an email

POST /api/v1/emails/send — queues a message to one or more recipients. The from address must be one of your verified senders.

Several recipients normally share one message, so they can see each other in the To: header. If your body carries an unsubscribe link, we send one message per recipient instead — each with its own working opt-out link and List-Unsubscribe header — and recipients no longer see each other. A link that identifies one person cannot live in a body several people share, which is the whole reason the two differ.

FieldTypeRequiredDescription
fromstringYesVerified sender, as “Name <email@domain>” or a bare address.
tostring[]YesOne or more recipient email addresses (up to 1000 per request). Sent as one shared message, or one message each when the body carries an unsubscribe link.
subjectstringYes*Required unless a template supplies one.
htmlstring—HTML body. Provide html or text (or a templateId).
textstring—Plain-text body / fallback. Auto-derived from html if omitted.
templateIdstring—Published template id. Uses the template's HTML + subject.
variablesobject—Key/value pairs interpolated into {{placeholders}}.
trackOpensboolean—Default false. Injects a 1×1 open-tracking pixel.
trackClicksboolean—Default false. Rewrites http(s) links for click tracking.
curl -X POST https://api.mailtr.co/api/v1/emails/send \
  -H "Authorization: Bearer mailtr_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Jane <jane@yourdomain.com>",
    "to": ["recipient@example.com"],
    "subject": "Welcome to Mailtr",
    "html": "<h1>Hello!</h1><p>Thanks for signing up.</p>",
    "trackOpens": true
  }'

A successful call returns 202 with the queued message id:

Response
{
  "success": true,
  "data": { "id": "0f8a…c21", "status": "queued" },
  "message": "Email queued successfully"
}

Sending with a template

Pass a published templateId instead of html. Any {{variables}} in the template are filled from the variables object. The template's subject is used unless you override it.

Write {{orderId}} anywhere in the template's content or subject, then declare it in the editor's Variables panel — that declaration is what decides how a send behaves when a value is missing:

CaseWhat happens
You supply a valueIt replaces the placeholder. Values are escaped, so they can't inject markup.
You omit it, but it has a defaultThe declared default is used.
You omit it, and it is marked requiredThe send is rejected (HTTP 400 / SMTP 550) rather than delivered with a hole in it.
The placeholder was never declaredIt is removed from the sent email — a raw {{token}} never reaches an inbox.

Links, images, lists and conditions

A variable works in a button's label and link, and in an image's source, alt text and link — use the { } button beside each field. A variable that starts a link is the whole link ({{ctaUrl}}); one inside it is URL-encoded (https://shop.example/o/{{orderId}}). A filled-in link must be http, https, mailto or tel, and an image http or https; anything else is removed. Links stay click-tracked.

Declare a List variable and choose it under a block's Show & repeat settings to repeat that block once per item, up to 100 — inside it, write {{item.name}}. Any block can also be shown only when a value has one, equals something, or is over or under a number. Send a list as a JSON array and an Object as a JSON object; values can be formatted with {{ total | currency: 'USD' }}, {{ name | default: 'there' }} or {{ sentAt | date: '%b %d' }}. Templates use Liquid syntax, and plain {{name}} variables work exactly as before.

variables
{
  "firstName": "Priya",
  "ctaUrl": "https://shop.example/orders/A-1042",
  "order": { "id": "A-1042", "total": 36 },
  "items": [
    { "name": "Mug", "price": 12, "image": "https://cdn.example/mug.png" },
    { "name": "Tea tin", "price": 24, "image": "https://cdn.example/tin.png" }
  ]
}

Editing a published template does not change what your sends use. Your changes are held in a working draft, and the live version keeps going out until you press Publish on the template's card.

curl -X POST https://api.mailtr.co/api/v1/emails/send \
  -H "Authorization: Bearer mailtr_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Jane <jane@yourdomain.com>",
    "to": ["recipient@example.com"],
    "templateId": "tpl_aB3xK9mZ",
    "variables": { "firstName": "Sam", "orderId": "1234" }
  }'

SMTP relay

Prefer SMTP? Generate SMTP credentials from Dashboard → SMTP & API and point any mail library at our relay. The exact host and port are shown on that page. SMTP sends share the same quota, logs, and stats as the HTTP API.

Hostsend.mailtr.co
Port587 (STARTTLS) or 465 (SSL/TLS)
Usernameyour generated SMTP username
Passwordyour generated SMTP password
Node.js (nodemailer)
import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "send.mailtr.co",
  port: 587,
  secure: false, // STARTTLS on 587; set true for 465
  auth: {
    user: process.env.MAILTR_SMTP_USER,
    pass: process.env.MAILTR_SMTP_PASS,
  },
});

await transporter.sendMail({
  from: '"Jane" <jane@yourdomain.com>',
  to: "recipient@example.com",
  subject: "Welcome to Mailtr",
  html: "<h1>Hello!</h1>",
});

Templates & variables over SMTP

SMTP has no request body to put a templateId in, so both travel as headers. They are the exact counterparts of the API's templateId and variables fields, and the rules in the table above apply identically.

HeaderValue
X-Mailtr-Template-IdPublished template id, e.g. tpl_aB3xK9mZ. The template's HTML and subject are used.
X-Mailtr-VariablesA JSON object, e.g. {"orderId":"1234"}. Malformed JSON is rejected with a 550 rather than sent with blank placeholders.

Your mail library may MIME-encode the variables header, since JSON is full of characters headers don't normally carry. That is fine — we decode it either way, so just pass the JSON as-is.

await transporter.sendMail({
  from: '"Jane" <jane@yourdomain.com>',
  to: "recipient@example.com",
  // No html/subject needed — the template supplies both.
  headers: {
    "X-Mailtr-Template-Id": "tpl_aB3xK9mZ",
    "X-Mailtr-Variables": JSON.stringify({
      firstName: "Sam",
      orderId: "1234",
    }),
  },
});

Tracking & bounces

Open and click tracking are opt-in per send (trackOpens / trackClicks), default off. When enabled we modify the sent HTML — a 1×1 pixel for opens, link rewriting for clicks — and surface the results in your Transactional logs and stats.

  • Tracking respects recipient consent: if any recipient of a send has opted out of tracking, both trackOpens and trackClicks are disabled for that entire send, even if you set them true.
  • Every send is recorded in Transactional → Logs with its delivery status (queued → sent → delivered → opened).
  • Hard bounces automatically add the recipient to your suppression list; soft bounces are recorded only.
  • Sends to a suppressed address are dropped, and a request whose recipients are all suppressed is rejected with 400.

Errors & limits

Errors return a non-2xx status with a JSON body describing the problem.

StatusMeaning
400Validation failed, missing content, or all recipients suppressed.
401Missing or invalid API key.
403Programmatic sending is not enabled for your account.
429Rate limit or per-email recipient limit exceeded (plan-based).

Full API Reference Coming Soon

Sending is live today. We are putting the finishing touches on the rest of our public API — soon you’ll be able to programmatically generate emails, manage templates, and sync subscribers.

  • Expected Q3 2026
  • Private beta full

Need help?

Join our community or contact support.

Visit Help Center