Templates
Which template is used
Section titled “Which template is used”Highest wins:
message— verbatim. No substitution, no escaping.template_<status>matching the reported status.message_template.- The built-in default.
An empty string counts as “not set”, so leaving an input blank falls through to the next level rather than sending an empty message.
The built-in default:
{{.StatusEmoji}} *{{.Workflow}}* on `{{.Repo}}`Status: {{.Status}}Branch: {{.Branch}} @ {{.ShortSha}}Actor: {{.Actor}}[Open run]({{.RunUrl}})Placeholders
Section titled “Placeholders”| Placeholder | Value |
|---|---|
{{.Repo}} |
owner/name of the repository |
{{.Workflow}} |
Workflow name |
{{.Job}} |
Job id inside the workflow |
{{.Status}} |
The reported status, verbatim |
{{.StatusEmoji}} |
✅ / ❌ / ⚠️ / ⏭ for the reported status |
{{.Actor}} |
User that triggered the run (git user.name outside Actions) |
{{.Ref}} |
Full ref, e.g. refs/heads/main |
{{.RefName}} |
Short ref name, e.g. main |
{{.Branch}} |
Alias of RefName |
{{.Sha}} |
Full commit SHA |
{{.ShortSha}} |
First 7 characters of the SHA |
{{.RunId}} |
Workflow run id (empty outside Actions) |
{{.RunNumber}} |
Workflow run number (empty outside Actions) |
{{.RunUrl}} |
Direct link to the run (empty outside Actions) |
{{.EventName}} |
Event that triggered the run (empty outside Actions) |
{{.ServerUrl}} |
GitHub server URL |
Inside a workflow the values come from the GITHUB_* environment. Outside one, the
repository-shaped fields are recovered from the local git checkout: Repo from the
origin remote, Branch and Ref from HEAD, Sha and ShortSha from the current
commit, Actor from git config user.name. The run-specific fields — RunId,
RunNumber, RunUrl, EventName — are empty, because there is no run.
A placeholder that is known but has no value renders as an empty string. A placeholder
that is not known at all is dropped, with an UNKNOWN_PLACEHOLDER:<name> warning — that
is your typo detector.
Anything that is not a well-formed {{.Name}} stays literal: {{.}}, {{.1Bad}},
{{Repo}} and {{.Repo} all come out exactly as written.
Escaping
Section titled “Escaping”Only substituted values are escaped. The template body goes through verbatim.
That split is what makes templates useful and safe at the same time: your template can
contain real *bold* and [links](...), while a branch named fix/a.b-c cannot inject
markup into the result.
notiflow render --template '*branch:* {{.Branch}}' --set Branch='fix/a.b-c'# *branch:* fix/a\.b\-cSee Parse modes for exactly what each mode escapes.
Length
Section titled “Length”Telegram allows 4096 UTF-16 code units. Most characters cost one; emoji outside the basic
plane cost two. A longer message is cut and ends with ....
The cut lands between markup tokens, never inside one: a MarkdownV2 escape pair, an HTML entity and an HTML tag are each indivisible. In HTML mode any tags still open at the cut are closed, so the truncated message is still valid markup rather than a 400 from Telegram.
Debugging a template
Section titled “Debugging a template”notiflow render --template '…' --explain--explain reports which template won, which parse mode is active, whether the text had
to be truncated, and which placeholders were unknown — before you send anything.