Skip to content

Template Functions

Sparrow provides 37 built-in template functions for transforming webhook payloads. These are available in subscription transform_template fields. They cover string manipulation, encoding, time formatting, arithmetic, and structural map/list building — enough to reshape a payload entirely without an embedded scripting runtime.

Templates use Go’s text/template syntax. The event payload is available as the template data context (e.g., {{ .payload.field_name }}).

FunctionDescription
jsonConvert value to JSON string
urlencodeURL-encode a string
base64Base64-encode a string
base64decodeBase64-decode a string
nowCurrent time
formatTimeFormat a time value
parseTimeParse a time string
upperUppercase string
lowerLowercase string
titleTitle case (capitalize first letter of each word)
trimTrim characters from both ends
trimSpaceTrim whitespace
splitSplit string into slice
joinJoin slice with separator
defaultDefault value for nil/empty
sliceSubstring by index
lenLength of string/slice/map
truncateTruncate string (hard cut)
ellipsisTruncate with ”…” suffix
repeatRepeat string N times
containsCheck if string contains substring
hasPrefixCheck string prefix
hasSuffixCheck string suffix
replaceReplace all occurrences
dictBuild a map from key/value pairs
listBuild a slice from arguments
appendAppend items to a slice
mergeMerge maps (later keys win)
addAdd two numbers
subSubtract two numbers
mulMultiply two numbers
divDivide two numbers
modInteger remainder
digSafe nested map lookup with default
toStringConvert any value to string
toIntConvert to integer
toFloatConvert to float

Converts any value to a JSON string.

{{ .data | json }}
{{ json .payload }}

Example:

Input: map[string]any{"name": "John", "age": 30}
Output: {"name":"John","age":30}

URL-encodes a string by escaping special characters.

{{ .email | urlencode }}
{{ urlencode "hello world" }}

Example:

Input: "hello world@example.com"
Output: "hello+world%40example.com"

Base64-encodes a string.

{{ .secret | base64 }}
{{ base64 "hello world" }}

Example:

Input: "hello world"
Output: "aGVsbG8gd29ybGQ="

Base64-decodes a string back to its original form.

{{ .encodedSecret | base64decode }}
{{ base64decode "aGVsbG8gd29ybGQ=" }}

Example:

Input: "aGVsbG8gd29ybGQ="
Output: "hello world"

Returns the current time as a time.Time object.

{{ now }}
{{ now | formatTime "2006-01-02 15:04:05" }}

Example:

Output: 2023-11-21 14:30:45 +0000 UTC

Formats a time value using Go’s time layout format.

{{ formatTime "2006-01-02" .createdAt }}
{{ .timestamp | formatTime "15:04:05" }}

Common layouts:

  • RFC3339: 2006-01-02T15:04:05Z07:00
  • Date only: 2006-01-02
  • Time only: 15:04:05
  • Human readable: January 2, 2006 at 3:04 PM

Parses a time string using Go’s time layout format.

{{ parseTime "2006-01-02" "2023-11-21" }}
{{ parseTime "15:04:05" .timeString }}

Example:

Input: "2006-01-02", "2023-11-21"
Output: 2023-11-21 00:00:00 +0000 UTC

Converts string to uppercase.

{{ .name | upper }}
{{ upper "hello world" }}

Example: "hello world" -> "HELLO WORLD"


Converts string to lowercase.

{{ .name | lower }}
{{ lower "HELLO WORLD" }}

Example: "HELLO WORLD" -> "hello world"


Capitalizes the first letter of each word; other characters are left unchanged.

{{ .name | title }}
{{ title "hello world" }}

Example: "hello world" -> "Hello World"


Trims specified characters from both ends of a string.

{{ trim " " .text }}
{{ trim "." "...hello..." }}

Example:

Input: " ", " hello world "
Output: "hello world"

Trims whitespace (spaces, tabs, newlines) from both ends.

{{ .userInput | trimSpace }}
{{ trimSpace " hello world " }}

Example: " hello world \n" -> "hello world"


Splits a string by separator into a slice.

{{ split "," "apple,banana,cherry" }}
{{ .tags | split "|" }}

Use with range to iterate:

{{range split "," .tags}}
- {{.}}
{{end}}

Joins a string slice with a separator.

{{ join ", " .tags }}
{{ .items | join " | " }}

Example: ["apple", "banana", "cherry"] -> "apple, banana, cherry"


Returns a default value if the input is nil or empty string.

{{ .optionalField | default "N/A" }}
{{ default "Unknown" .name }}

Subscriptions render strictly by default (template_missing_key: error), so reading a key the payload does not have fails before default runs. For a field that may be absent, read it with dig or index first: {{ dig "plan" "free" .payload }} or {{ default "free" (index .payload "plan") }}.

Example:

Input: "N/A", "" -> "N/A"
Input: "N/A", "John" -> "John"

Returns a substring from start to end index (end exclusive). Use -1 for end of string.

{{ slice 0 5 "hello world" }}
{{ .text | slice 2 8 }}

Example:

Input: 0, 5, "hello world" -> "hello"
Input: 6, -1, "hello world" -> "world"

Returns the length of a string, slice, or map.

{{ len .name }}
{{ .items | len }}

Truncates a string to a maximum length (hard cut, no ellipsis).

{{ truncate 10 .longText }}
{{ .description | truncate 50 }}

Example: truncate 10 "this is a very long string" -> "this is a "


Truncates a string to a maximum length and adds ... if truncated.

{{ ellipsis 20 .longText }}
{{ .description | ellipsis 100 }}

Example:

ellipsis 20 "this is a very long string that needs truncation"
-> "this is a very lo..."
ellipsis 10 "short"
-> "short"

If maxLen <= 3, no ellipsis is added.


Repeats a string a specified number of times.

{{ repeat 3 "*" }}
{{ .pattern | repeat 5 }}

Example: repeat 3 "*" -> "***"


Checks if a string contains a substring (case-sensitive).

{{ if contains "error" .message }}
Error found!
{{ end }}

Checks if a string starts with a prefix (case-sensitive).

{{ if hasPrefix "http" .url }}
Valid URL
{{ end }}

Checks if a string ends with a suffix (case-sensitive).

{{ if hasSuffix ".jpg" .filename }}
Image file
{{ end }}

Replaces all occurrences of a substring with another.

{{ replace " " "_" .name }}
{{ .text | replace "foo" "bar" }}

Example: replace " " "_" "hello world test" -> "hello_world_test"


Builds a map from alternating key/value pairs. Keys must be strings. Pipe through json to emit a structured object without hand-writing braces (which avoids quoting/escaping bugs).

{{ dict "user" .payload.id "amount" .payload.amount | json }}

Example: dict "a" 1 "b" 2 | json -> {"a":1,"b":2}


Builds a slice from its arguments. Pipe through json to emit an array.

{{ list .payload.a .payload.b | json }}

Example: list 1 2 3 | json -> [1,2,3]


Appends items to a slice, returning the new slice. Combine with = reassignment inside range to accumulate an array.

{{ $out := list }}
{{ range .payload.items }}{{ $out = append $out .id }}{{ end }}
{{ $out | json }}

Merges maps into a new map. Later keys overwrite earlier ones; inputs are not modified.

{{ merge .payload (dict "source" "sparrow") | json }}

Arithmetic on numbers. Values are coerced from JSON numbers or numeric strings. add, sub, mul, and div return floats; mod returns an integer. div and mod error on a zero divisor.

{{ add .payload.subtotal .payload.tax }}
{{ mul .payload.amount 100 }}
{{ div .payload.amount_cents 100 }}
{{ mod .payload.sequence 10 }}

Example: mul 5.5 100 -> 550


Safely reads a nested value from a map by a path of keys, returning the default if any key along the path is missing. Arguments are one or more keys, then a default value, then the map (last). Avoids template errors on optional fields.

{{ dig "customer" "address" "city" "unknown" .payload }}

Example: on {"customer":{"id":"c1"}}, dig "customer" "email" "none" .payload -> "none"


Type conversion. toString renders any value as a string; toInt and toFloat coerce JSON numbers or numeric strings (toInt truncates toward zero).

{{ toString .payload.count }}
{{ dict "count" (toInt .payload.count) | json }}
{{ toFloat .payload.price }}