Templates
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.
01 Create a template
/v1/templatesGive 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.
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"]02 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
missingarray when you render or preview — so you can catch gaps before sending.
data.03 Render and preview
Two endpoints render without sending — wire them into a live editor or a test before you ship copy.
/v1/templates/{id}/renderRender a saved template by id or slug with a data object. Returns the resolved { subject, html, text, missing }.
// 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{
"subject": "Welcome to Acme, Ada",
"html": "<h1>Hi Ada</h1><p>Thanks for joining Acme.</p>",
"text": "Hi Ada — thanks for joining Acme.",
"missing": []
}/v1/templates/previewPreview 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.
// 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"]04 Send by templateId
/v1/emailsTo 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.
await drin.emails.send({
from: { email: "hello@acme.com" },
to: [{ email: "ada@example.com" }],
templateId: "welcome", // id or slug
data: { company: "Acme", firstName: "Ada" },
});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.05 Template gallery
/v1/templates/galleryThe 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.
const starters = await drin.templates.gallery();
for (const t of starters) {
console.log(t.category, t.name, t.subject);
}06 Fields
{{variables}}.html, text, or both.name when omitted.