Docs/Sending email

Send email as Markdown

Write the body of a message in Markdown and the Anypost TypeScript SDK renders it to email-safe HTML and a plain-text alternative for you. You skip the table layouts and inline-style workarounds that hand-written HTML email demands.

Note

Markdown rendering is a feature of the TypeScript SDK only. It runs on emailmd, which depends on Node 20+ and does not run in edge or browser runtimes. Every other way to send accepts the already-rendered html/text you produce however you like.

Why Markdown

HTML email is its own dialect. Layouts are built from nested tables, styles have to be inlined, and every client renders them a little differently. Writing that by hand, or keeping a templating pipeline that emits it, is the tax of sending good-looking mail.

Markdown moves that work to render time. You write prose; emailmd produces a complete, client-tested HTML document and a matching plain-text part in one step. The plain-text body is generated automatically, so the message degrades cleanly where HTML is stripped.

Install

emailmd is an optional peer dependency. Install it alongside the SDK only if you send Markdown:

npm install anypost emailmd

A Markdown send without emailmd installed throws a clear error telling you to add it. Nothing else in the SDK requires it.

Send Markdown

Pass markdown in place of html and text. The SDK renders it before the request and sends the result:

import { Anypost } from "anypost";
 
const anypost = new Anypost("ap_your_api_key");
 
await anypost.email.send({
  from: "Acme <[email protected]>",
  to: ["[email protected]"],
  subject: "Welcome aboard",
  markdown: [
    "# Welcome aboard",
    "",
    "Thanks for signing up. Your workspace is ready.",
    "",
    "[Open your dashboard](https://app.example.com)",
  ].join("\n"),
});

markdown cannot be combined with html or text; choose one content source. Everything else on the message — from, to, attachments, tags, headers — behaves exactly as it does on a normal send. See Send a single email.

Preheader and frontmatter

A YAML frontmatter block sets the preheader, the preview line shown in the inbox before a message is opened, and carries any custom keys you want to read back:

await anypost.email.send({
  from: "[email protected]",
  to: ["[email protected]"],
  subject: "Your March report",
  markdown: [
    "---",
    "preheader: 12% more opens than February",
    "---",
    "# Your March report",
    "",
    "The numbers are in.",
  ].join("\n"),
});

Theme it

Pass render options through markdownOptions to set a brand color, fonts, and the rest of the theme:

await anypost.email.send({
  from: "[email protected]",
  to: ["[email protected]"],
  subject: "Welcome aboard",
  markdown: "# Welcome aboard\n\nGlad you are here.",
  markdownOptions: {
    theme: { brandColor: "#4f46e5" },
  },
});

In a batch, set markdownOptions once on defaults to apply one theme to every entry's Markdown:

await anypost.email.sendBatch({
  defaults: { markdownOptions: { theme: { brandColor: "#4f46e5" } } },
  emails: [
    { from, to: ["[email protected]"], subject: "Hi A", markdown: "# Hi A" },
    { from, to: ["[email protected]"], subject: "Hi B", markdown: "# Hi B" },
  ],
});

The full theme shape, the wrapper and font options, validation levels, and the Markdown extensions emailmd supports are covered in the emailmd docs.

Render once, reuse

To render the same Markdown for many recipients, or to inspect the output before sending, call renderMarkdown directly. It returns the HTML, the plain-text alternative, and the extracted frontmatter:

import { renderMarkdown } from "anypost";
 
const { html, text, meta } = await renderMarkdown("# Hi\n\nOne render, many sends.");
 
console.log(meta.preheader);
// pass html/text into as many sends as you like

Where to go next