# POST /templates/preview

Part of the SendHeron REST API. Base URL: `https://api.sendheron.com/api/v1`

Render unsaved HTML or a block document exactly as a send would compose it, without saving a template.

```http
POST https://api.sendheron.com/api/v1/templates/preview
Authorization: Bearer <YOUR_API_KEY>
```

**Required scopes:** `templates:read`

**Success status:** `201`

**Rate limit:** 100/min per key, counted against the organization's READ ceiling (1200/min).

**Body parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `bodyHtml` | string | no | Raw HTML to preview. Send exactly one of `bodyHtml` or `document`. |
| `document` | object | no | Block document to compile and preview. Send exactly one of `bodyHtml` or `document`. |
| `emailType` | MARKETING \| TRANSACTIONAL | no | Which type to preview as, which decides whether an unsubscribe is owed. Omitting it previews as marketing: an unknown type never weakens compliance. |
| `channel` | CAMPAIGN \| BULK \| SEQUENCE \| DIRECT \| TRANSACTIONAL_API | no | Which channel to preview through. A channel can only tighten the policy, never loosen it. Defaults to `DIRECT`. |
| `sampleData` | object | no | Values for the Handlebars variables, so the preview renders real-looking content instead of empty placeholders. |

**Endpoint-specific errors** (beyond the shared error set):

- `400`: Neither or both of `bodyHtml` and `document` were supplied. A `null` or blank `bodyHtml` counts as absent, matching the save path.
- `400`: `templates.renderFailed`: the body failed to compile as a Handlebars template. The preview fails exactly as the send would.

> This is here to show you what you did not write. The preview composes through the same path a real send uses, so it includes the unsubscribe block marketing mail gains and the effect of the channel floor: preview a `TRANSACTIONAL` template with `channel: CAMPAIGN` and the unsubscribe still appears, because `CAMPAIGN` and `BULK` are always marketing whatever the template says. Neither of those is something a client-side renderer can show you, which is the argument for previewing here instead of rendering blocks in your own UI. The response carries the composed `renderedHtml` alongside four authoring flags, so you can tell what the pipeline did rather than diffing HTML to find out: `injectedUnsubscribeFooter`, `hasUnrecognizedUnsubscribeLink`, `appendedPromoBadge`, and `unsubscribeVariableIgnored`. That last one is the one to watch when porting a template in from another provider: it means the template references `{{unsubscribe_url}}` but its policy never supplies that variable, so a real send renders the link empty. Link click tracking is the one send-time step a preview does not apply.


---

Rate limits: 100/min per key, counted against the organization's READ ceiling (1200/min). Both windows are one minute; there is no hourly or daily quota.
