# Update template

`PUT https://postcraft.io/api/v1/templates/{id}`

Updates the template. Writes the published copy by default, so the change is live on the next render; pass `stage: "draft"` to stage it for a later publish instead. Switching from blocks to code is irreversible. Pass `folder` to move the template: a path like `marketing/onboarding` (missing folders are created), or `null` to unfile it to the root: folder moves require an API key. Bearer tokens must match the template and must not be readonly.

## Authentication

Send one of these headers:

- `x-api-key: <key>`: Long-lived account credential. Create one from the dashboard.
- `Authorization: Bearer <token>`: Short-lived presigned token scoped to a single template (minted via /presigned-url).

## Parameters

| Name | In | Type | Required | Description |
| ---- | -- | ---- | -------- | ----------- |
| `id` | path | string | yes |  |

## Request body

`content-type: application/json`

| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `name` | string | no |  |
| `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 or null | no | Folder path to move the template to, e.g. "marketing/onboarding". Segments are created if missing; `null` unfiles it to the root; omit to leave the folder unchanged. Requires an API key. |
| `stage` | object | no |  |
| `ifUnmodifiedSince` | string | no | The `updatedAt` from your last read or write of this template. The write is refused with 409 if anyone has written since, so re-read and reapply your change. Omit it to overwrite whatever is there. |

Example:

```json
{
  "name": "welcome",
  "code": "Hello {{firstName}}, welcome to {{product}}!",
  "exampleContext": {
    "firstName": "Ada",
    "product": "Postcraft"
  }
}
```

## Responses

### 200

Template updated.

| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `updatedAt` | string | yes |  |
| `publishedAt` | string | yes | When the published copy last changed. Unmoved by a `draft` write. |
| `changed` | boolean | yes | False when the write stored nothing new, because the body restated what was already there. |
| `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
