# Render template

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

Renders the stored template against a runtime context and returns one self-contained HTML string, with the rendered `subject` beside it. The subject is made safe for a single email header line: line breaks, other whitespace runs and control characters collapse to one space, the ends are trimmed, and it is cut to 998 characters, so a context value cannot add a header. `target` picks between `browser` (default) and `email`; `wrap` picks between a full `document` (default) and a `fragment`. When `target` is `email`, the response also includes a `text` plain-text alternative for the text part of a multipart message.

## 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 |
| ----- | ---- | -------- | ----------- |
| `context` | object | no | Structural JSON value: object or array. Scalars and null are rejected. |
| `target` | `"browser"`, `"email"` | no |  |
| `wrap` | `"document"`, `"fragment"` | no |  |

Example:

```json
{
  "context": {
    "name": "Ada",
    "product": "Postcraft"
  },
  "target": "email",
  "wrap": "document"
}
```

## Responses

### 200

Rendered HTML.

| Field | Type | Required | Description |
| ----- | ---- | -------- | ----------- |
| `html` | string | yes |  |
| `text` | string | no | Plain-text alternative. Present only when `target` is "email". |
| `subject` | string or null | yes | The subject line rendered against the context, or null when the template has none or it renders to nothing. It is safe to pass as a single header line: every run of line breaks, other whitespace or control characters becomes one space, the ends are trimmed, and it is cut to 998 characters. |
| `publishedAt` | string | yes | When the published copy this render came from last changed. A caller that keeps something derived from a template, such as a review of its content, can compare this against the value it stored instead of fetching the template again. |

### 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
