# Templates & the CLI

> Push your React Email folder to Mailmanny with one command, then send templates by id.

Source: https://www.mailmanny.com/docs/templates
All docs for AI agents: https://www.mailmanny.com/llms.txt

Keep your emails in code, as React Email components in your repo. One command renders the whole folder and uploads it to Mailmanny, where you can send it by name and preview it in the app.

## Push your React Email folder

Run this in your project (where `react` and `@react-email/render` are installed). The key needs the **Push templates** permission, and pushing templates is on paid plans (Essentials and up, from $9 a month). Sending React Email with `emails.send` works on every plan.

```bash
MAILMANNY_API_KEY=mm_live_… npx mailmanny templates push
```

**Output**

```text
Mailmanny pushing 3 templates from emails → https://www.mailmanny.com/api/v1

  ✓ welcome         created  4.1 KB  “Welcome to Manny's Bakery”
  ✓ receipt         updated  6.8 KB  “Your receipt”
  ✓ auth.reset      created  1.2 KB  “Reset your password”

3 pushed. Use them with template_id: "<id>" when sending.
```

For every email file the CLI:

1. compiles it with your project's own esbuild (or tsx, if that's what you have), using your tsconfig paths;
2. renders the default export with your `@react-email/render`, passing the component's `PreviewProps`;
3. uploads the HTML and a plain-text version as a template named after the file.

Pushing again updates the template in place, so it's safe to run on every deploy. The CLI reads `MAILMANNY_API_KEY` (and `MAILMANNY_BASE_URL`) from your shell, `.env.local` or `.env`.

> **Note:** No esbuild in your project?
> Install it as a dev dependency: `npm install -D esbuild`. React Email's own preview server depends on it, so most projects already have it.

### Which files become templates

```text
emails/
├── welcome.tsx          → welcome
├── receipt.tsx          → receipt
├── auth/
│   └── reset.tsx        → auth.reset
├── _components/         (skipped: starts with _)
│   └── footer.tsx
└── static/              (skipped)
    └── logo.png
```

Every `.tsx`, `.jsx`, `.ts` or `.js` file with a default-exported component is a template. Its id is its path without the extension, with folders joined by dots. Folders starting with `_` or `.`, plus `static` and `components`, are skipped, like React Email's preview does.

### What a template file can export

**emails/welcome.tsx**

```tsx
// emails/welcome.tsx
import { Body, Button, Container, Heading, Html, Preview, Text } from "@react-email/components";

// Optional: the default subject (a string, or a function of the props)
export const subject = "Welcome to Manny's Bakery";

// Optional: the template's display name and preview text in Mailmanny
export const meta = { name: "Welcome email", previewText: "Your first croissant is on us" };

export default function WelcomeEmail({ firstName }: { firstName: string }) {
  return (
    <Html>
      <Preview>Your first croissant is on us</Preview>
      <Body>
        <Container>
          <Heading>Welcome, {firstName}!</Heading>
          <Text>Show this email at the counter for a free croissant.</Text>
          <Button href="https://mannysbakery.com/menu">See the menu</Button>
        </Container>
      </Body>
    </Html>
  );
}

// The CLI renders with these props. Merge tags stay in the HTML and are filled in per email at send time.
WelcomeEmail.PreviewProps = { firstName: "{{first_name|friend}}" };
```

- `default` (component, required): The email. Rendered with PreviewProps.
- `subject` (string | (props) => string): The template's default subject.
- `meta.name` (string): Display name in Mailmanny. Default: the file name, humanized.
- `meta.previewText` (string): Preview text shown in the template list.
- `meta.props` (object): Props to render with instead of PreviewProps.

### Personalize with merge tags

Templates render once, so per-person values are merge tags: `{{first_name}}`, `{{email}}`, `{{fields.COMPANY}}`, with an optional fallback after a pipe: `{{first_name|friend}}`. Put them in `PreviewProps` as shown above. When you send through the API, `variables` fill them in; in campaigns and automations, each contact's own data does.

### Options

```bash
npx mailmanny templates push --dir src/emails     # a different folder
npx mailmanny templates push --only welcome,receipt   # just these
npx mailmanny templates push --dry-run              # render and check, upload nothing
npx mailmanny templates list                        # what's in your workspace
```

### Push from CI

Create a key with only the Push templates permission and store it as a CI secret:

**.github/workflows/email-templates.yml**

```yaml
name: Push email templates
on:
  push:
    branches: [main]
    paths: ["emails/**"]
jobs:
  push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22, cache: npm }
      - run: npm ci
      - run: npx mailmanny templates push
        env:
          MAILMANNY_API_KEY: ${{ secrets.MAILMANNY_TEMPLATES_KEY }}
```

## Send a template

```ts
await mm.emails.send({
  from: "Manny's Bakery <hello@mannysbakery.com>",
  to: "sam@example.com",
  templateId: "welcome",
  variables: { first_name: "Sam" },
});
```

See [Sending email](https://www.mailmanny.com/docs/sending.md#templates) for details. Pushed templates also show up in [Templates](https://www.mailmanny.com/templates) in the app, ready to use in campaigns.

## Templates API

`GET /api/v1/templates` (permission: `templates:read`)

`GET /api/v1/templates/{id_or_external_id}` (permission: `templates:read`)

`PUT /api/v1/templates/{external_id}` (permission: `templates:write`)

`DELETE /api/v1/templates/{id_or_external_id}` (permission: `templates:write`)

`PUT` creates or replaces an HTML template (201 when new, 200 when updated). It takes `html` (required), `text` (generated from the HTML if left out), `name`, `subject` and `preview_text`. Templates built in the drag-and-drop editor can be read and sent, but not overwritten: a `PUT` to one returns 409 `conflict`.

**Node.js SDK**

```tsx
await mm.templates.push({
  externalId: "welcome",
  subject: "Welcome to Manny's Bakery",
  react: <WelcomeEmail firstName="{{first_name|friend}}" />,
});
```

**curl**

```bash
curl -X PUT https://www.mailmanny.com/api/v1/templates/welcome \
  -H "Authorization: Bearer $MAILMANNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome email",
    "subject": "Welcome to Manny'"'"'s Bakery",
    "html": "<html>…</html>",
    "text": "Welcome! …"
  }'
```
