# Create template

`POST https://postcraft.io/api/v1/templates`

Creates a template in visual mode (when `blocks` is supplied) or code mode (when `code` is supplied without `blocks`). Pass an optional `folder` path (e.g. `marketing/onboarding`) to file it: missing folders along the path are created, and the response echoes the resolved folder. The folder is unrelated to the template name. Requires an API key: presigned tokens are template-scoped and there is no template yet to scope to.

## Authentication

Send one of these headers:

- `x-api-key: <key>`: Long-lived account credential. Create one from the dashboard.

## Request body

`content-type: application/json`

| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `name` | string | yes |  |
| `code` | string | no |  |
| `styles` | string | no |  |
| `exampleContext` | object | no | Structural JSON value: object or array. Scalars and null are rejected. |
| `blocks` | object | no | Block tree (visual mode). Authored via the editor; rarely hand-written. See the Postcraft dashboard's editor for the full discriminated union of block kinds. |
| `subject` | string or null | no | Email subject line, as Handlebars rendered against the render context. `null` removes it; omit it to keep the stored one. |
| `folder` | string | no | Folder path to file the template under, e.g. "marketing/onboarding". Segments are created if missing. Unrelated to the template name. |

Example:

```json
{
  "name": "welcome-email",
  "code": "Hello {{name}}, welcome to {{product}}!",
  "exampleContext": {
    "name": "Ada",
    "product": "Postcraft"
  },
  "folder": "marketing/onboarding"
}
```

## Responses

### 200

Template created.

| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `template` | object | yes |  |
| `folder` | object or null | yes |  |
| `diagnostics` | object[] | yes | Problems found in the template. Advisory rather than fatal: a template that renders can still carry warnings, while an `error` severity is one that will not. |

### Errors

Every error answers with `{ "error": { "code": string, "message": string } }`.

| Status | Meaning |
| ------ | ------- |
| 400 | Invalid input. |
| 401 | Missing or invalid credential. |
| 403 | Authenticated but not allowed. |
| 404 | Not found. |
| 413 | Payload exceeds size limit. |
| 422 | Template render failed. |
| 429 | Rate limited. |

The whole API as an OpenAPI document: https://postcraft.io/api/v1/openapi.json
