---
title: "Create a template"
description: "A draft unless you ask for it to be published. A draft can be previewed and cannot be sent."
url: "https://openemail.uk/docs/api/templates/create"
area: "API"
category: "Mailbox"
---

# Create a template

A draft unless you ask for it to be published. A draft can be previewed and cannot be sent.

`POST /templates`

## POST /templates

A draft unless you ask for it to be published. A draft can be previewed and cannot be sent.

## Example

Needs `templates:write`. Returns 201. `slug` is derived from the name when you omit it, and it is the handle to pin in code, because a name is the thing somebody renames.

**curl**

```
curl -X POST "$OE/templates" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{
    "name": "Order shipped",
    "engine": "html",
    "subject": "Order {{orderId}} is on its way",
    "html": "<p>Hello {{customer}}, {{orderId}} left the warehouse.</p>",
    "props": [
      { "key": "orderId", "kind": "text", "required": true },
      { "key": "customer", "kind": "text", "default": "there" }
    ],
    "publish": true
  }'
```

**Response**

```
{
  "object": "template",
  "id": "tpl_9c1f0a4b7e05d3862c1f0a44",
  "name": "Order shipped",
  "slug": "order-shipped",
  "description": null,
  "status": "active",
  "publishedVersion": 1,
  "latestVersion": 1,
  "subject": "Order {{orderId}} is on its way",
  "engine": "html",
  "slots": [],
  "props": [
    { "key": "orderId", "kind": "text", "required": true, "default": null },
    { "key": "customer", "kind": "text", "required": false, "default": "there" }
  ],
  "createdAt": "2026-08-30T10:41:02.000Z",
  "updatedAt": "2026-08-30T10:41:02.000Z"
}
```

> Set `publish: true` from a deploy script. Without it you get version 1 as a draft, which is right when a person is going to read the copy first and wrong in anything automated.

> A duplicate name is `template_name_taken` and a duplicate slug is `template_slug_taken`, both 409s on the connection rather than on the account.

> A connection holds 200 templates. The 201st is `template_limit_reached`, a guard against a runaway script, not a plan limit.
