# MJML (alpha) API

> Validate and render MJML and read the MJML reference. In alpha, open to the Emailit team only.

Base URL: `https://api.emailit.com/v2`. Authenticate with `Authorization: Bearer <API key>`.

## Validate MJML — POST /mjml/validate

> Check MJML the way a template save does, without saving it. Returns every diagnostic with its line. Alpha: open to the Emailit team only.

# Validate MJML

Checks MJML the way a template save does (XML syntax, MJML structure, attribute types and Temple syntax) and compiles it with MJML 5.4.1. Nothing is saved. Invalid MJML still returns `200`, with `valid: false` and the diagnostics. Requires the `full` scope.

See [Validation](/docs/templates/mjml/#validation) for what each check covers.

> **MJML is in alpha:** MJML is open only to the Emailit team while we test it. Other requests, including every request made with an API key, return `403` with `error: "mjml_alpha"`. See [Who can use MJML](/docs/templates/mjml/#who-can-use-mjml).

`POST /mjml/validate`

## Body parameters

- `source` (string | object, required): The MJML to check: MJML markup (`<mjml>…</mjml>`), MJML JSON, or an Emailit MJML document, such as a template's stored `source`. See [Source formats](/docs/templates/mjml/#source-formats). Maximum 2 MB.

**Request** `POST /mjml/validate`

**Node.js**

```javascript
const source = `<mjml>
  <mj-body>
    <mj-column>
      <mj-text colour="#333333">Hi {{first_name}}</mj-text>
    </mj-column>
  </mj-body>
</mjml>`;

const response = await fetch('https://api.emailit.com/v2/mjml/validate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ source })
});

const { valid, diagnostics } = await response.json();
```

**cURL**

```bash
curl -X POST https://api.emailit.com/v2/mjml/validate \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "<mjml>\n  <mj-body>\n    <mj-column>\n      <mj-text colour=\"#333333\">Hi {{first_name}}</mj-text>\n    </mj-column>\n  </mj-body>\n</mjml>"
  }'
```

**200 Invalid**

```json
{
  "valid": false,
  "mjml_version": "5.4.1",
  "format": "markup",
  "diagnostics": [
    {
      "severity": "error",
      "code": "mjml.invalid-child",
      "message": "<mj-column> cannot be placed inside <mj-body>. Allowed parents: <mj-group>, <mj-section>.",
      "line": 3,
      "column": 6,
      "tag": "mj-column",
      "path": [0, 0]
    },
    {
      "severity": "error",
      "code": "mjml.unknown-attribute",
      "message": "<mj-text> has no attribute colour. Did you mean color?",
      "line": 4,
      "column": 16,
      "tag": "mj-text",
      "attribute": "colour",
      "path": [0, 0, 0]
    },
    {
      "severity": "warning",
      "code": "mjml.missing-title",
      "message": "Add an <mj-title> to <mj-head>; clients and screen readers use it.",
      "line": 1,
      "column": 2,
      "tag": "mjml",
      "path": []
    },
    {
      "severity": "info",
      "code": "mjml.missing-preview",
      "message": "Add an <mj-preview> to control the inbox preview text.",
      "line": 1,
      "column": 2,
      "tag": "mjml",
      "path": []
    }
  ]
}
```

**200 Valid**

```json
{
  "valid": true,
  "mjml_version": "5.4.1",
  "format": "markup",
  "diagnostics": []
}
```

**200 Unrecognized**

```json
{
  "valid": false,
  "mjml_version": "5.4.1",
  "format": null,
  "diagnostics": [
    {
      "severity": "error",
      "code": "document.unrecognized",
      "message": "Expected MJML markup (<mjml>…), MJML JSON or an Emailit MJML document."
    }
  ]
}
```

**422**

```json
{
  "message": "Validation failed",
  "errors": {
    "source": ["The source field is required."]
  }
}
```

**403**

```json
{
  "error": "mjml_alpha",
  "message": "MJML is in alpha and available only to Emailit platform admins."
}
```

## Returns

Returns `200 OK` with the result, even when the MJML is invalid:

| Field | Type | Description |
| --- | --- | --- |
| `valid` | boolean | `true` when there are no error diagnostics, so a template with this source can be saved. |
| `mjml_version` | string | The MJML version Emailit compiles with. |
| `format` | string or null | How the source was read: `markup` or `json`. `null` when it couldn't be read. |
| `diagnostics` | array | Every error, warning and info diagnostic. |

`valid: true` means a template with this source would be accepted. Fix the warnings too: they usually point at real problems, such as a missing `<mj-title>`. To get the HTML, use [Render MJML](/docs/api-reference/mjml/render/).

Returns `422` with `message: "Validation failed"` when `source` is missing or blank, and `403` with `error: "mjml_alpha"` without MJML access.

### Diagnostic object

Fields that don't apply are left out.

| Field | Type | Description |
| --- | --- | --- |
| `severity` | string | `error`, `warning` or `info`. Only errors make `valid` false. |
| `code` | string | A stable code, for example `mjml.invalid-child`, `xml.unclosed-tag` or `temple.unclosed-if`. See [Diagnostic codes](/docs/templates/mjml/#diagnostic-codes). |
| `message` | string | A readable explanation. |
| `line` | integer | 1-based line in the markup. Markup sources only. |
| `column` | integer | 1-based column in the markup. Markup sources only. |
| `tag` | string | The element the diagnostic is about. |
| `attribute` | string | The attribute the diagnostic is about. |
| `path` | integer[] | Child-index path from the `<mjml>` root. |

---
Source: https://emailit.com/docs/api-reference/mjml/validate/

## Render MJML — POST /mjml/render

> Compile MJML to the HTML Emailit sends and preview it with Temple variables. Alpha: open to the Emailit team only.

# Render MJML

Compiles MJML to the HTML Emailit sends, with MJML 5.4.1. With `variables`, the response also includes `rendered_html`: the HTML after Temple is applied, as one recipient would get it. Nothing is saved or sent. Requires the `full` scope.

This endpoint is limited to 120 requests per minute.

> **MJML is in alpha:** MJML is open only to the Emailit team while we test it. Other requests, including every request made with an API key, return `403` with `error: "mjml_alpha"`. See [Who can use MJML](/docs/templates/mjml/#who-can-use-mjml).

`POST /mjml/render`

## Body parameters

- `source` (string | object, required): The MJML to compile: MJML markup (`<mjml>…</mjml>`), MJML JSON, or an Emailit MJML document, such as a template's stored `source`. See [Source formats](/docs/templates/mjml/#source-formats). Maximum 2 MB.

- `variables` (object): Temple variables for `rendered_html`, such as `first_name`, or a `cf` object with campaign custom fields. Nothing is filled in automatically: pass the variables the sending channel would provide. See [Variables by channel](/docs/templates/mjml/#variables-by-channel). Must be an object; anything else returns `400`.

**Request** `POST /mjml/render`

**Node.js**

```javascript
const source = `<mjml>
  <mj-head>
    <mj-title>Welcome</mj-title>
    <mj-preview>Your account is ready</mj-preview>
  </mj-head>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text>Hi {{first_name|"there"}}, welcome aboard.</mj-text>
        <mj-button href="{{activation_url}}">Activate account</mj-button>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>`;

const response = await fetch('https://api.emailit.com/v2/mjml/render', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    source,
    variables: {
      first_name: 'Ada',
      activation_url: 'https://example.com/activate?token=abc123'
    }
  })
});

const { html, rendered_html } = await response.json();
```

**cURL**

```bash
curl -X POST https://api.emailit.com/v2/mjml/render \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "<mjml><mj-head><mj-title>Welcome</mj-title><mj-preview>Your account is ready</mj-preview></mj-head><mj-body><mj-section><mj-column><mj-text>Hi {{first_name|\"there\"}}, welcome aboard.</mj-text><mj-button href=\"{{activation_url}}\">Activate account</mj-button></mj-column></mj-section></mj-body></mjml>",
    "variables": {
      "first_name": "Ada",
      "activation_url": "https://example.com/activate?token=abc123"
    }
  }'
```

**200**

```json
{
  "valid": true,
  "mjml_version": "5.4.1",
  "html": "<!doctype html>\n<html lang=\"und\" dir=\"auto\" …>…Hi {{first_name|\"there\"}}, welcome aboard.…<a href=\"{{activation_url}}\" …>…</html>\n",
  "html_bytes": 5267,
  "diagnostics": [],
  "rendered_html": "<!doctype html>\n<html lang=\"und\" dir=\"auto\" …>…Hi Ada, welcome aboard.…<a href=\"https://example.com/activate?token=abc123\" …>…</html>\n"
}
```

**200 Invalid**

```json
{
  "valid": false,
  "mjml_version": "5.4.1",
  "html": null,
  "html_bytes": 0,
  "diagnostics": [
    {
      "severity": "error",
      "code": "mjml.invalid-child",
      "message": "<mj-column> cannot be placed inside <mj-body>. Allowed parents: <mj-group>, <mj-section>.",
      "line": 3,
      "column": 6,
      "tag": "mj-column",
      "path": [0, 0]
    },
    {
      "severity": "error",
      "code": "mjml.unknown-attribute",
      "message": "<mj-text> has no attribute colour. Did you mean color?",
      "line": 4,
      "column": 16,
      "tag": "mj-text",
      "attribute": "colour",
      "path": [0, 0, 0]
    },
    {
      "severity": "warning",
      "code": "mjml.missing-title",
      "message": "Add an <mj-title> to <mj-head>; clients and screen readers use it.",
      "line": 1,
      "column": 2,
      "tag": "mjml",
      "path": []
    },
    {
      "severity": "info",
      "code": "mjml.missing-preview",
      "message": "Add an <mj-preview> to control the inbox preview text.",
      "line": 1,
      "column": 2,
      "tag": "mjml",
      "path": []
    }
  ],
  "rendered_html": null
}
```

**422**

```json
{
  "message": "Validation failed",
  "errors": {
    "source": ["The source field is required."]
  }
}
```

**403**

```json
{
  "error": "mjml_alpha",
  "message": "MJML is in alpha and available only to Emailit platform admins."
}
```

## Returns

Returns `200 OK` with the result, even when the MJML is invalid:

| Field | Type | Description |
| --- | --- | --- |
| `valid` | boolean | `true` when there are no error diagnostics. |
| `mjml_version` | string | The MJML version Emailit compiles with. |
| `html` | string or null | The compiled HTML, with Temple tags left in place, as Emailit stores it for a template. `null` when the MJML has errors. |
| `html_bytes` | integer | UTF-8 size of `html`. Gmail clips messages over about 102 KB. `0` when `html` is `null`. |
| `diagnostics` | array | Every error, warning and info diagnostic. See [Validate MJML](/docs/api-reference/mjml/validate/). |
| `rendered_html` | string or null | `html` with `variables` applied by Temple. Only present when you send `variables`, and `null` when the MJML has errors. |

Invalid MJML returns `200` with `valid: false`, `html: null` and the diagnostics. `rendered_html` uses exactly the `variables` you send: missing variables render as empty strings or as their default, so `{{first_name|"there"}}` renders `there`. Plain text isn't generated from MJML.

Returns `422` with `message: "Validation failed"` when `source` is missing or blank, `400` when `variables` isn't an object, and `403` with `error: "mjml_alpha"` without MJML access.

---
Source: https://emailit.com/docs/api-reference/mjml/render/

## Retrieve the MJML reference — GET /mjml/reference

> Every MJML component and attribute Emailit supports, the editor versions, the Temple guide and authoring rules. Alpha: open to the Emailit team only.

# Retrieve the MJML reference

Retrieves every MJML component and attribute of the MJML version Emailit compiles with, the current editor versions, the Temple guide and authoring rules. Use it to build tooling or to give an AI model what it needs to write valid MJML templates. Requires the `full` scope.

The same reference is available to AI agents as the `get-mjml-reference` MCP tool. See [MJML for AI agents](/docs/templates/mjml/#mjml-for-ai-agents).

> **MJML is in alpha:** MJML is open only to the Emailit team while we test it. Other requests, including every request made with an API key, return `403` with `error: "mjml_alpha"`. See [Who can use MJML](/docs/templates/mjml/#who-can-use-mjml).

`GET /mjml/reference`

**Request** `GET /mjml/reference`

**Node.js**

```javascript
const response = await fetch('https://api.emailit.com/v2/mjml/reference', {
  headers: {
    'Authorization': 'Bearer your_api_key'
  }
});

const reference = await response.json();
```

**cURL**

```bash
curl https://api.emailit.com/v2/mjml/reference \
  -H "Authorization: Bearer your_api_key"
```

**200**

```json
{
  "mjml_version": "5.4.1",
  "document_schema_version": 1,
  "editors": {
    "mjml-code": { "version": "0.2.0", "stage": "alpha" },
    "mjml-visual": { "version": "0.2.0", "stage": "alpha" }
  },
  "components": [
    {
      "tag": "mj-body",
      "label": "Body",
      "description": "The visible email. Holds sections, wrappers and heroes.",
      "category": "root",
      "ending_tag": false,
      "parents": ["mjml"],
      "children": ["mj-raw", "mj-section", "mj-wrapper", "mj-hero"],
      "attributes": [
        { "name": "width", "type": "unit(px)", "default": "600px" },
        { "name": "background-color", "type": "color", "default": null },
        { "name": "id", "type": "string", "default": null },
        { "name": "mj-class", "type": "string", "default": null },
        { "name": "css-class", "type": "string", "default": null }
      ]
    },
    {
      "tag": "mj-button",
      "label": "Button",
      "description": "A bulletproof call-to-action button. Content is the label.",
      "category": "content",
      "ending_tag": true,
      "parents": ["mj-column", "mj-hero"],
      "children": [],
      "attributes": [
        { "name": "align", "type": "enum(left,center,right)", "default": "center" },
        { "name": "background-color", "type": "color", "default": "#414141" },
        { "name": "href", "type": "string", "default": null },
        { "name": "inner-padding", "type": "unit(px,%){1,4}", "default": "10px 25px" }
      ]
    }
  ],
  "temple_guide": "Temple is Emailit's templating language, evaluated per recipient at send time on the final HTML:\n- {{first_name}} inserts a variable; …",
  "rules": "MJML rules:\n- Structure: <mjml> > <mj-head> (optional) + <mj-body>. …",
  "reference_text": "MJML 5.4.1 component reference. Format: name=type[default]. …"
}
```

**403**

```json
{
  "error": "mjml_alpha",
  "message": "MJML is in alpha and available only to Emailit platform admins."
}
```

## Returns

Returns `200 OK` with the reference. The example above is shortened: `components` lists all 36 components with all of their attributes.

| Field | Type | Description |
| --- | --- | --- |
| `mjml_version` | string | The MJML version Emailit compiles with. |
| `document_schema_version` | integer | The current `schema_version` of the stored MJML document. |
| `editors` | object | The current version and stage (`alpha`, `beta` or `stable`) of `mjml-code` and `mjml-visual`. |
| `components` | array | Every MJML component. See below. |
| `temple_guide` | string | How Temple works inside MJML: variables, defaults, conditionals and campaign variables. |
| `rules` | string | MJML authoring rules: structure, allowed attributes and accessibility. |
| `reference_text` | string | The whole component reference as compact plain text, for prompts. |

Returns `403` with `error: "mjml_alpha"` without MJML access.

### Component object

| Field | Type | Description |
| --- | --- | --- |
| `tag` | string | Tag name, for example `mj-button`. |
| `label` | string | Display name. |
| `description` | string | What the component does. |
| `category` | string | `root`, `head`, `layout`, `content`, `interactive`, `child` or `advanced`. |
| `ending_tag` | boolean | `true` when the content is raw HTML or text (`mj-text`, `mj-button`, `mj-raw`, …). |
| `parents` | string[] | Elements this component can be placed in. |
| `children` | string[] | Elements this component can contain. `["*"]` means any component (`mj-attributes`). |
| `attributes` | array | Attributes, each with `name`, `type` (an MJML type such as `color`, `unit(px,%){1,4}` or `enum(left,center,right)`) and `default` (`null` when there's none). |

---
Source: https://emailit.com/docs/api-reference/mjml/reference/
