Versioning & git
Docs / Templates

Versioning & git

Keep templates in a repository you already review and deploy from. Connect it once per account; every push becomes an immutable version, and every message records the version and the commit it rendered from.

Repository layout

templates/
  welcome.twig            body (Twig) — or welcome.liquid
  welcome.de-AT.twig      welcome for locale de-AT
  welcome.de.twig         welcome for locale de
  welcome.text.twig       optional plain-text part
  welcome.json            optional sidecar
layouts/
  base.twig               optional; the body replaces {# body #}

The file name is the slug. The extension picks the engine; a sidecar that disagrees is an error, not a guess. A retention number above your plan ceiling is lowered to it, never refused. Everything is compile-checked at sync, and one bad file holds back the whole push — a sync is a single transaction.

A send of welcome with locale de-AT looks for welcome.de-AT, then welcome.de-at, then welcome.de, then welcome. The file is that slug plus the extension, so the Twig file is welcome.de-AT.twig and the Liquid file would be welcome.de-AT.liquid. A slug that already names a locale, such as welcome.de, is used as written and does not fall back.

A sidecar is optional, JSON or YAML. These are the keys a sync reads. variables_schema is accepted in place of variables. engine must be twig or liquid and must agree with the extension. layout is a slug under layouts/, and that file must contain exactly one {# body #}.

templates/welcome.json
{
  "subject": "Hello {{ name }}",
  "name": "Welcome",
  "description": "Sent when an account is created.",
  "variables": { "required": ["name"] },
  "is_sensitive": false,
  "content_retention_days": 30,
  "engine": "twig",
  "layout": "base"
}

Connecting a repository

  1. Templates → Sync from git → paste the SSH clone URL, the branch, and an optional folder inside the repo.
  2. We generate an ed25519 deploy key. Add the public half to the repository as a read-only deploy key (GitHub: Settings → Deploy keys; GitLab: Settings → Repository → Deploy keys). The private half never leaves our keyring.
  3. Add the push webhook URL shown once at connect. The token in the URL is the secret; the body is ignored. Without it, changes still arrive on the hourly fetch.
  4. Press Sync now.

We only ever read. The remote must be a public host — nothing on a private network — and an HTTPS URL is accepted only for a public repository, never with a token in it.

What a sync does

A new version is minted only when the subject, a body, the engine or the variables changed; a metadata-only edit updates the template without a version. Versions are immutable and never deleted. Deleting the file and pushing leaves the template sendable. The sync counts it as an orphan and does not remove it. A push does not retire a template, and the panel and the API have no retire action. A template created in the panel is never overwritten by a file of the same slug; the sync stops and says so.

HONESTY NOTE — WHAT WE RECORDEach version stores the last commit that touched its file. The Message Log shows that sha next to the version a message rendered from, so “what exactly did it say” has a git answer.