> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drin.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Templates

> Store reusable email bodies with {{merge}} variables, preview them with sample data, and send by templateId — so your copy lives in one place, not your code.

Store an email body once and send it by id or slug. Merge variables fill in the per-recipient details, so your copy lives on the platform — editable without a deploy — instead of buried in your code.

A template bundles a `subject` and an `html` and/or `text` body. Anywhere you write `{{variable}}`, the rendering engine substitutes a value from the `data` object you pass at send time. Templates are scoped to the sending project and addressable by their auto-generated `id` or a human-friendly `slug`.

## Create a template

**`POST /v1/templates`**

Give it a `name`, a `subject`, and a body. An optional `slug` gives you a stable handle like `welcome` to reference in code; omit it and one is generated from the name. The response includes a `variables` array — every `{{token}}` the engine found across the subject and bodies.

<CodeGroup>
  ```typescript Node.js theme={null}
  import { DrinClient } from "@drin00/sdk";

  const drin = new DrinClient({ apiKey: process.env.DRIN_API_KEY });

  const template = await drin.templates.create({
    name: "Welcome",
    slug: "welcome",
    subject: "Welcome to {{company}}, {{firstName}}",
    html: "<h1>Hi {{firstName}}</h1><p>Thanks for joining {{company}}.</p>",
    text: "Hi {{firstName}} — thanks for joining {{company}}.",
  });

  console.log(template.id, template.slug, template.variables);
  // "tmpl_…", "welcome", ["company", "firstName"]
  ```

  ```bash cURL theme={null}
  curl https://api.drin.run/v1/templates \
    -H "Authorization: Bearer $DRIN_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Welcome",
      "slug": "welcome",
      "subject": "Welcome to {{company}}, {{firstName}}",
      "html": "<h1>Hi {{firstName}}</h1><p>Thanks for joining {{company}}.</p>",
      "text": "Hi {{firstName}} — thanks for joining {{company}}."
    }'
  ```
</CodeGroup>

## Merge variables

Variables use double-brace `{{handlebars}}` syntax and can appear in the subject, the HTML, and the text part. At send (or render) time, pass a `data` object whose keys match the token names:

* `Subject: "Welcome to {{company}}, {{firstName}}"` + `data: { company: "Acme", firstName: "Ada" }` → `"Welcome to Acme, Ada"`.
* A token with no matching key renders empty and is reported in the `missing` array when you render or preview — so you can catch gaps before sending.

<Note>
  **Values are escaped for HTML.** Merge values are safe-rendered into the HTML body, so user-supplied data can't inject markup. Author the layout in the template; pass only data through `data`.
</Note>

## Render and preview

Two endpoints render without sending — wire them into a live editor or a test before you ship copy.

**`POST /v1/templates/{id}/render`**

**Render** a *saved* template by id or slug with a `data` object. Returns the resolved `{ subject, html, text, missing }`.

```typescript Render a saved template theme={null}
// Render a saved template with sample data — no email is sent.
const rendered = await drin.templates.render("welcome", {
  company: "Acme",
  firstName: "Ada",
});

console.log(rendered.subject); // "Welcome to Acme, Ada"
console.log(rendered.missing); // [] — variables with no value supplied
```

```json Render response theme={null}
{
  "subject": "Welcome to Acme, Ada",
  "html": "<h1>Hi Ada</h1><p>Thanks for joining Acme.</p>",
  "text": "Hi Ada — thanks for joining Acme.",
  "missing": []
}
```

**`POST /v1/templates/preview`**

**Preview** an *unsaved* draft — pass the `subject`/`html`/`text` inline along with `data`. Ideal for a template editor that re-renders on every keystroke without persisting anything.

```typescript Preview a draft theme={null}
// Preview an UNSAVED draft straight from your editor.
const draft = await drin.templates.preview({
  subject: "Hi {{firstName}}",
  html: "<p>Welcome to {{company}}.</p>",
  data: { firstName: "Ada" }, // company intentionally omitted
});

console.log(draft.html);    // "<p>Welcome to .</p>"
console.log(draft.missing); // ["company"]
```

## Send by templateId

**`POST /v1/emails`**

To send a stored template, set `templateId` (id or slug) on a normal send and supply the merge values in `data`. The engine resolves the subject and body server-side.

<CodeGroup>
  ```typescript Node.js theme={null}
  await drin.emails.send({
    from: { email: "hello@acme.com" },
    to: [{ email: "ada@example.com" }],
    templateId: "welcome", // id or slug
    data: { company: "Acme", firstName: "Ada" },
  });
  ```

  ```bash cURL theme={null}
  curl https://api.drin.run/v1/emails \
    -H "Authorization: Bearer $DRIN_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "from": { "email": "hello@acme.com" },
      "to": [{ "email": "ada@example.com" }],
      "templateId": "welcome",
      "data": { "company": "Acme", "firstName": "Ada" }
    }'
  ```
</CodeGroup>

<Warning>
  **templateId and inline bodies don't mix.** A send carries either a `templateId` or inline `html`/`text` — not both. Combining them is a `validation_error`. To override just the subject, pass `subject` alongside `templateId`; it is rendered with `data` and wins over the template's subject.
</Warning>

## Template gallery

**`GET /v1/templates/gallery`**

The gallery is a set of curated starter templates — welcome emails, receipts, password resets, and the like — each with a category, a ready-made body, and `sampleData`. Use one as the starting point for a `create` call instead of authoring from a blank page.

```typescript Browse the gallery theme={null}
const starters = await drin.templates.gallery();

for (const t of starters) {
  console.log(t.category, t.name, t.subject);
}
```

## Fields

<ParamField body="name" type="string" required>
  A human label for the template.
</ParamField>

<ParamField body="subject" type="string" required>
  The subject line. May contain `{{variables}}`.
</ParamField>

<ParamField body="html" type="string">
  The HTML body with optional merge variables. Provide `html`, `text`, or both.
</ParamField>

<ParamField body="text" type="string">
  The plain-text body / alternative part.
</ParamField>

<ParamField body="slug" type="string">
  A stable, URL-safe handle to reference the template in code. Generated from `name` when omitted.
</ParamField>

<ParamField body="data" type="object">
  At render/send time: the merge values keyed by variable name.
</ParamField>

## Next

<CardGroup cols={2}>
  <Card title="Send email" icon="bolt" href="/sending">
    The send fields a `templateId` plugs into.
  </Card>

  <Card title="Batch & scheduled" icon="bolt" href="/sending/batch">
    Reuse one template across a batch of recipients.
  </Card>

  <Card title="Create template API" icon="code" href="/api-reference/templates/create">
    The full `POST /v1/templates` contract.
  </Card>

  <Card title="Contacts" icon="inbox" href="/contacts">
    Pull merge data from your address book.
  </Card>
</CardGroup>
