Placeholders & Templates
Actions in the Event Manager can use dynamic placeholders — variables that are replaced at runtime with data from the triggering event. Placeholders follow the Go template syntax {{.FieldName}} and can be used in HTTP request bodies, email subjects and bodies, command environment variables, filesystem action paths, and more.
SFTPGo uses Go's text/template engine under the hood. Conditions, loops, and variable assignments are fully supported.
Available placeholders
Core fields
| Placeholder | Type | Description |
|---|---|---|
{{.Name}} |
string | Username for filesystem events; virtual folder name, admin username, or domain name depending on the trigger type. |
{{.ExtName}} |
string | External username. Set to the email address used for authenticating public shares configured with email authentication. |
{{.Event}} |
string | Event name — e.g., upload, download for filesystem events, or add, update, delete for provider events. |
{{.Status}} |
integer | Status code for filesystem events: 1 = success, 2 = generic error, 3 = quota exceeded. |
{{.Errors}} |
list of strings | Error details, if any. Use {{ stringJoin .Errors ", " }} to format as a single string. |
{{.VirtualPath}} |
string | Path as seen by the user — e.g., /documents/report.pdf. |
{{.FsPath}} |
string | Full filesystem path — e.g., /home/user/documents/report.pdf or the equivalent cloud storage key. |
{{.VirtualTargetPath}} |
string | Virtual target path for rename and copy operations. |
{{.FsTargetPath}} |
string | Full filesystem target path for rename and copy operations. |
{{.ObjectName}} |
string | File or directory name (e.g., report.pdf), or the provider object name. For data retention actions, this is the username of the affected user. |
{{.ObjectType}} |
string | Object type for provider events: user, group, admin, folder, share, etc. |
{{.FileSize}} |
int64 | File size in bytes. |
{{.Elapsed}} |
int64 | Elapsed time in milliseconds for filesystem events. |
{{.Protocol}} |
string | Protocol used — e.g., SFTP, FTP, HTTP, WebDAV. |
{{.IP}} |
string | Client IP address. |
{{.Role}} |
string | User or admin role. |
{{.Email}} |
string | For filesystem events: the email of the user performing the action. For provider events: the email of the affected user or admin. Blank in other cases. |
{{.UID}} |
string | Unique event identifier. |
Timestamp
{{.Timestamp}} is a time object with the following methods:
| Method | Description | Example output |
|---|---|---|
.UTC |
Time in UTC | — |
.Local |
Time in server's local timezone | — |
.Unix |
Unix timestamp in seconds | 1751644200 |
.UnixMilli |
Unix timestamp in milliseconds | 1751644200000 |
.Year, .Month, .Day |
Date components | 2025, July, 4 |
.Hour, .Minute, .Second |
Time components | 14, 30, 0 |
.Format "layout" |
Custom format using Go's reference time 2006-01-02 15:04:05 |
See examples below |
Format examples:
{{ .Timestamp.Format "2006-01-02" }}outputs2025-07-04{{ .Timestamp.Format "02/01/2006 15:04" }}outputs04/07/2025 14:30{{ .Timestamp.UTC.Format "2006-01-02T15:04:05.000" }}outputs2025-07-04T12:30:00.000
Object and Initiator
{{.Object}}
The provider object associated with the event, with sensitive fields removed. It exposes a JSON method and typed accessors for inner objects:
{{.Object.JSON}}— JSON representation of the full object.{{.Object.Share}}— Share data, when the event involves a share. Example:{{.Object.Share.ExpiresAt}}.{{.Object.User}}— User data. Example:{{.Object.User.Email}}.{{.Object.Admin}}— Admin data. Example:{{.Object.Admin.Description}}.{{.Object.Group}}— Group data.
Field names match those documented in the REST API, using PascalCase (e.g., CreatedAt, Description).
To embed the JSON in another JSON structure:
- As a nested JSON object:
"data": {{.Object.JSON}} - As a JSON string:
"data": {{.Object.JSON | toJson}}
{{.Initiator}}
For provider events, exposes the entity (user or admin) that initiated the action — not the object being acted upon. For example, when an admin creates a user, .Object is the new user and .Initiator is the admin.
Resolution rules:
- Share events: the user who owns the share.
- Other provider events (user/admin/group creation, update, deletion): the admin who performed the action.
- Self-edit operations: the same entity as
.Object(the user or admin editing their own profile).
The initiator is resolved lazily — SFTPGo only queries the database if the template actually references it.
Typed accessors:
{{.Initiator.User}}— Initiator as a user. Example:{{.Initiator.User.Email}}.{{.Initiator.Admin}}— Initiator as an admin. Example:{{.Initiator.Admin.Email}}.{{.Initiator.JSON}}— JSON representation.
Calling the wrong accessor (e.g.,
.Initiator.User when the initiator is an admin) causes a template rendering error. Use conditional checks:
{{if .Initiator.User}}User: {{.Initiator.User.Email}}{{end}}
{{if .Initiator.Admin}}Admin: {{.Initiator.Admin.Email}}{{end}}
Collection placeholders
These placeholders are populated by specific action types and are available to subsequent actions in the same rule.
{{.RetentionChecks}}
Populated by the Data retention check action. List of retention check results, one per user. Each item contains:
| Field | Type | Description |
|---|---|---|
Username |
string | The user whose files were checked. |
Email |
list of strings | The user's email addresses. |
ActionName |
string | The name of the retention action. |
Type |
integer | 0 = delete, 1 = archive. |
Results |
list of objects | Per-folder results (see below). |
Each item in Results:
| Field | Type | Description |
|---|---|---|
Path |
string | Folder path. |
Retention |
integer | Retention threshold in hours. |
DeletedFiles |
integer | Number of files removed. |
DeletedSize |
integer | Total bytes removed. |
Elapsed |
integer | Processing time in nanoseconds. |
Info |
string | Additional information. |
Error |
string | Error message, if any. |
{{.RetentionReports}} is also available as an email attachment or HTTP multipart file containing compressed CSV reports.
{{.ShareExpirationChecks}}
Populated by the Share expiration check action. List of results grouped by user. Each item contains:
| Field | Type | Description |
|---|---|---|
User |
object | The user who owns the shares. Fields match the REST API in PascalCase. |
Results |
list of objects | Per-share results (see {{.ShareExpirationResult}} below). |
{{.ShareExpirationResult}}
Available only when Split events is enabled for the share expiration check. In this mode, each result triggers its own event, and standard placeholders like {{.Name}} and {{.Email}} are automatically set to the user associated with this specific result.
| Field | Type | Description |
|---|---|---|
Share |
object | The full share object (PascalCase fields). Example: {{.ShareExpirationResult.Share.Name}}. |
Action |
integer | 1 = notify (advance warning), 2 = delete. |
Reason |
string | max_tokens, expiration_date, or inactivity. |
Expiration |
time object | The calculated expiration timestamp. |
{{.EventReports}}
Populated by the Event report action. See the Event Report documentation for the full field reference.
Other placeholders
| Placeholder | Type | Description |
|---|---|---|
{{.IDPFields}} |
object | Custom fields from the Identity Provider. Structure depends on your IdP configuration. Example: {{.IDPFields.sftpgo_role}}. |
{{.Metadata}} |
map of strings | Cloud storage metadata (key/value pairs). Use range to iterate, or {{ toJson .Metadata }} for JSON output. |
{{.Shares}} |
lazy object | Shares associated with the file path of a filesystem event. Call .Load to retrieve them. Example: {{ range .Shares.Load }}{{ range .Options.Emails }}{{ . }},{{ end }}{{ end }}. |
Go template built-ins
In addition to the SFTPGo-specific helpers documented below, every built-in function and action from Go's text/template package is available.
Formatting and printing
| Function | Description | Example |
|---|---|---|
printf |
Formats using Go's fmt verbs and returns the result. | {{ printf "%s uploaded %d bytes" .Name .FileSize }} |
print |
Concatenates values using default formatting. | {{ print .Name " at " .IP }} |
println |
Like print, but adds a trailing newline. |
{{ println .VirtualPath }} |
Common printf verbs:
%s— string%d— integer (%05dzero-pads to width 5, e.g.00042)%v— default format for any value%q— Go-quoted string (escapes quotes and special characters)%x/%X— lowercase / uppercase hexadecimal%.2f— floating point with 2 decimal places
Comparison and logic
| Function | Description |
|---|---|
eq, ne |
Equal, not equal. eq accepts multiple arguments: eq a b c is true if a == b || a == c. |
lt, le, gt, ge |
Less-than, less-or-equal, greater-than, greater-or-equal. |
and, or, not |
Logical operators. and / or short-circuit and return the first non-empty / non-zero argument. |
Example:
{{if and (eq .Event "upload") (gt .FileSize 1048576) }}
Large file uploaded by {{.Name}}
{{end}}
Collection operations
| Function | Description | Example |
|---|---|---|
len |
Length of a string, slice, map, or array. | {{ len .Errors }} |
index |
Returns the element at the given index or key. | {{ index .Errors 0 }}, {{ index .IDPFields "sftpgo_role" }} |
slice |
Slices a string, slice, or array. | {{ slice .Name 0 3 }} |
Escaping
| Function | Description |
|---|---|
js |
Escapes for use inside a JavaScript string literal. |
html |
HTML-escapes <, >, &, and quotes. |
For URL escaping, use SFTPGo's urlEscape / urlPathEscape helpers (documented below).
Control structures
Conditional blocks, loops, and variable assignment are template actions (not functions):
{{if eq .Status 1}}Success{{else}}Failed{{end}}
{{range .Errors}}
- {{.}}
{{end}}
{{range $i, $item := .RetentionChecks}}
Check {{$i}}: {{$item.Username}}
{{end}}
{{with .Object.User}}
User email: {{.Email}}
{{end}}
{{$label := "Upload"}}
{{$label}}: {{.VirtualPath}}
{{if}}— conditional block; pair with{{else}}/{{else if}}.{{range}}— iterates over slices, arrays, or maps.{{break}}and{{continue}}are supported.{{with}}— narrows the.context for the block, skipping it if the value is empty.$var := value— declares a variable scoped to the surrounding block.$var = value— reassigns an existing variable.
For the complete reference, see the text/template documentation.
Helper functions
Helper functions transform or format placeholder values. They can be called in two equivalent ways:
- Direct:
{{ toJson .VirtualPath }} - Piped:
{{ .VirtualPath | toJson }}
The pipe syntax is especially useful for chaining multiple transformations.
Encoding and conversion
| Function | Description | Example |
|---|---|---|
toJson |
Converts any value to its JSON representation. Strings are quoted, special characters escaped. | {{ toJson .VirtualPath }} → "/dir/file.txt" |
toJsonUnquoted |
Like toJson, but strips surrounding quotes from string values. Other types behave like toJson. |
{{ toJsonUnquoted .ObjectName }} → file.txt |
toBase64 |
Encodes a string as Base64. | {{ toBase64 .Name }} |
toHex |
Encodes a string as hexadecimal. | {{ toHex .Name }} |
URL encoding
| Function | Description | Example |
|---|---|---|
urlEscape |
Encodes a string for use in query parameters. | {{ urlEscape .Email }} → user%40example.com |
urlPathEscape |
Encodes a string for use in URL path segments. | {{ urlPathEscape .VirtualPath }} → folder%20name%2Ffile.txt |
Path manipulation
| Function | Description | Example |
|---|---|---|
pathDir |
Returns the directory portion of a path. | {{ pathDir "/a/b/file.txt" }} → /a/b |
pathBase |
Returns the last element of a path. | {{ pathBase "/a/b/file.txt" }} → file.txt |
pathExt |
Returns the file extension. | {{ pathExt "/a/b/file.txt" }} → .txt |
pathJoin |
Joins path segments into a clean virtual path. Takes a string slice. | {{ pathJoin (stringSlice "/a" .VirtualPath "final") }} |
filePathJoin |
Like pathJoin but uses OS-specific separators. Use for .FsPath values. |
{{ filePathJoin (stringSlice "/data" .FsPath) }} |
String operations
| Function | Description | Example |
|---|---|---|
stringSlice |
Creates a list of strings. Useful as input for pathJoin or stringJoin. |
{{ stringSlice "a" "b" "c" }} |
stringJoin |
Joins a list of strings with a separator. | {{ stringJoin .Errors ", " }} |
stringTrimSuffix |
Removes a suffix if present. | {{ stringTrimSuffix .VirtualPath ".jpg" }} |
stringTrimPrefix |
Removes a prefix if present. | {{ stringTrimPrefix .VirtualPath "/data" }} |
stringReplace |
Replaces all occurrences of a substring. | {{ stringReplace .VirtualPath "/dir1" "/dir2" }} |
stringHasPrefix |
Returns true if the string starts with the given prefix. | {{if stringHasPrefix .VirtualPath "/dir"}}...{{end}} |
stringHasSuffix |
Returns true if the string ends with the given suffix. | {{if stringHasSuffix .VirtualPath ".csv"}}...{{end}} |
stringContains |
Returns true if the string contains the given substring. | {{if stringContains .VirtualPath "report"}}...{{end}} |
stringToLower |
Converts to lowercase. | {{ stringToLower .VirtualPath }} |
stringToUpper |
Converts to uppercase. | {{ stringToUpper .Name }} |
slicesContains |
Returns true if a slice contains the given element. | {{if slicesContains .Errors "timeout"}}...{{end}} |
Maps and formatting
| Function | Description | Example |
|---|---|---|
createDict |
Creates a map from alternating key-value pairs. | {{ $m := createDict 1 "OK" 2 "KO" }} |
mapToString |
Looks up a value in a map by key. | {{ mapToString .Status $statusMap }} |
humanizeBytes |
Formats a byte count as a human-readable string (KB, MB, GB, etc.). | {{ humanizeBytes .FileSize }} → 10 KB |
fromMillis |
Converts a Unix timestamp in milliseconds to a time object. | {{ (fromMillis $admin.CreatedAt).Format "2006-01-02" }} |
fromNanos |
Converts a Unix timestamp in nanoseconds to a time object. | {{ (fromNanos $event.Timestamp).Format "15:04:05" }} |
Template examples
Building a JSON HTTP request body
{{- $statusMap := createDict 1 "OK" 2 "KO" -}}
{
"Name": {{.Name | toJson}},
"VirtualPath": {{.VirtualPath | toJson}},
"Status": {{.Status}},
"StatusString": {{ (mapToString .Status $statusMap) | toJson }},
"Metadata": {{.Metadata | toJson}},
"ObjectString": {{.Object.JSON | toJson}},
"ObjectJSON": {{.Object.JSON}}
}
NameandVirtualPathare converted to JSON strings withtoJson, ensuring proper quoting and escaping.Statusis output as a raw integer.StatusStringmaps the integer to a human-readable label viacreateDict+mapToString, then quotes it.Metadataoutputs the full metadata map as a JSON object.ObjectStringis the object's JSON wrapped as a JSON string (double-encoded).ObjectJSONis the raw JSON object embedded directly.
In Go templates,
{{- and -}} trim whitespace before/after the tag. Use them to keep the output clean.
Dynamic user creation from Identity Provider
{{- $keyPrefix := stringJoin (stringSlice "users" .Name) "/" -}}
{
"username": {{toJson .Name}},
"status": 1,
"permissions": {"/":["*"]},
"filesystem": {
"provider": 1,
"s3config": {
"bucket": "default",
"region": "default",
"key_prefix": {{ $keyPrefix | toJson }}
}
},
"groups": [
{{- $roles := .IDPFields.sftpgo_role -}}
{{- range $i, $role := $roles -}}
{{- if ne $i 0}},{{end}}
{"type": {{if eq $i 0}}1{{else}}2{{end}},
"name": {{$role | toJson}}}
{{- end}}
]
}
This template generates a user configuration for the Identity Provider account check action:
$keyPrefixis built by joining"users"and the username with/— e.g.,users/alice.- The
groupsarray is populated from thesftpgo_roleclaim. The first role gets type1(primary group), subsequent roles get type2(secondary). - All string values use
toJsonfor safe JSON encoding.