Docs
Templating
Dynamic values with Go templates, Sprig functions, and template context
Templating
Use Go templates and Sprig functions to create dynamic values in your playbooks.
Overview
Quadrastack supports Go template syntax with the Sprig function library. Access variables, session data, and request context using template expressions, and transform values with built-in functions.
Basic Templating
requests:
get-user:
method: GET
url: "{{.vars.baseUrl}}/users/{{.vars.userId}}"
headers:
Authorization: "Bearer {{.vars.token}}"
Template Syntax
Basic Expression
Use {{ and }} to delimit template expressions.
# Variable reference
url: "{{.vars.baseUrl}}/api"
# Function call
header: "{{uuidv4}}"
# Pipeline
timestamp: "{{now | date \"2006-01-02\"}}"
Template Context
Three main context objects are available:
.vars- Variables from vars profiles.session- Session data from workflow steps.request- Current request context (headers, body, etc.)
Context: .vars
Access variables from your vars profiles.
Basic Variables
vars:
default:
baseUrl: https://api.example.com
apiKey: test-key-123
userId: 42
requests:
get-user:
method: GET
url: "{{.vars.baseUrl}}/users/{{.vars.userId}}"
headers:
X-API-Key: "{{.vars.apiKey}}"
Nested Variables
vars:
default:
api:
baseUrl: https://api.example.com
version: v1
timeout: 30
user:
name: John Doe
email: john@example.com
requests:
get-data:
method: GET
url: "{{.vars.api.baseUrl}}/{{.vars.api.version}}/data"
body:
userName: "{{.vars.user.name}}"
userEmail: "{{.vars.user.email}}"
Array Variables
vars:
default:
userIds: [1, 2, 3, 4, 5]
tags: [api, test, smoke]
requests:
get-first-user:
method: GET
url: "{{.vars.baseUrl}}/users/{{index .vars.userIds 0}}"
tagged-request:
method: GET
url: /api/data
headers:
X-Tags: "{{join \",\" .vars.tags}}"
Context: .session
Access response data from previous workflow steps.
Session Structure
When you capture a response with out in a workflow step:
workflows:
auth-flow:
steps:
- request: login
out: auth # Captures response in .session.auth
The session object contains:
.session.<name>.status- HTTP status code.session.<name>.headers.<HeaderName>- Response headers.session.<name>.body.<jsonPath>- Response body (JSON).session.<name>.bodyRaw- Raw response body (string)
Accessing Status
workflows:
check-flow:
steps:
- request: create-user
out: created
- request: verify-status
expect:
body:
$.previousStatus: "{{.session.created.status}}"
Accessing Headers
workflows:
header-flow:
steps:
- request: login
out: auth
- request: next-request
headers:
X-Session-ID: "{{.session.auth.headers.X-Session-ID}}"
X-Rate-Limit: "{{.session.auth.headers.X-RateLimit-Remaining}}"
Accessing Body (JSON)
workflows:
user-flow:
steps:
- request: login
out: auth
- request: get-profile
url: "{{.vars.baseUrl}}/users/{{.session.auth.body.userId}}"
headers:
Authorization: "Bearer {{.session.auth.body.token}}"
- request: update-profile
body:
userId: "{{.session.auth.body.userId}}"
oldEmail: "{{.session.auth.body.user.email}}"
Accessing Raw Body
workflows:
raw-flow:
steps:
- request: get-xml
out: xmlResponse
- request: send-raw
body: "{{.session.xmlResponse.bodyRaw}}"
Nested JSON Paths
workflows:
nested-flow:
steps:
- request: get-data
out: data
- request: use-nested
body:
# Access nested fields
userName: "{{.session.data.body.user.profile.name}}"
firstTag: "{{index .session.data.body.tags 0}}"
metadata: "{{.session.data.body.metadata.timestamp}}"
Context: .request
Access current request context (less commonly used).
requests:
echo-headers:
method: POST
url: /api/echo
headers:
X-Original-Path: "{{.request.path}}"
Note: .request context is primarily for advanced use cases. Most templates use .vars and .session.
Sprig Functions
Quadrastack includes the Sprig function library with 100+ template functions.
String Functions
uuidv4
Generate a random UUID v4.
headers:
X-Request-ID: "{{uuidv4}}"
# Output: X-Request-ID: f47ac10b-58cc-4372-a567-0e02b2c3d479
upper / lower
Convert to uppercase/lowercase.
headers:
X-Environment: "{{.vars.environment | upper}}"
X-Name: "{{.vars.userName | lower}}"
# Input: environment: production, userName: JohnDoe
# Output: X-Environment: PRODUCTION, X-Name: johndoe
trim / trimSuffix / trimPrefix
Trim whitespace or specific strings.
body:
cleanName: "{{.vars.userName | trim}}"
noPrefix: "{{.vars.url | trimPrefix \"https://\"}}"
noSuffix: "{{.vars.filename | trimSuffix \".json\"}}"
replace
Replace occurrences of a string.
body:
sanitized: "{{.vars.text | replace \" \" \"_\"}}"
# Input: text: "hello world"
# Output: sanitized: "hello_world"
quote / squote
Add quotes around strings.
body:
quoted: "{{.vars.value | quote}}"
singleQuoted: "{{.vars.value | squote}}"
# Input: value: test
# Output: quoted: "test", singleQuoted: 'test'
contains / hasPrefix / hasSuffix
String matching.
# Used in assertions or conditionals
body:
isProduction: "{{.vars.environment | hasPrefix \"prod\"}}"
Date Functions
now
Current timestamp.
headers:
X-Timestamp: "{{now | date \"2006-01-02T15:04:05Z07:00\"}}"
X-Unix: "{{now | unixEpoch}}"
X-Date: "{{now | date \"2006-01-02\"}}"
# Output:
# X-Timestamp: 2024-01-15T10:30:00Z
# X-Unix: 1705318200
# X-Date: 2024-01-15
date
Format a timestamp.
Go date format: Use 2006-01-02 15:04:05 as reference
headers:
# ISO 8601
X-ISO: "{{now | date \"2006-01-02T15:04:05Z07:00\"}}"
# US format
X-US: "{{now | date \"01/02/2006\"}}"
# Custom
X-Custom: "{{now | date \"Mon Jan 2 15:04:05 MST 2006\"}}"
# Time only
X-Time: "{{now | date \"15:04:05\"}}"
unixEpoch
Unix timestamp (seconds since epoch).
body:
timestamp: "{{now | unixEpoch}}"
# Output: timestamp: 1705318200
dateModify
Add or subtract time.
body:
tomorrow: "{{now | dateModify \"24h\" | date \"2006-01-02\"}}"
nextWeek: "{{now | dateModify \"168h\" | date \"2006-01-02\"}}"
yesterday: "{{now | dateModify \"-24h\" | date \"2006-01-02\"}}"
Encoding Functions
b64enc / b64dec
Base64 encoding/decoding.
headers:
Authorization: "Basic {{printf \"%s:%s\" .vars.username .vars.password | b64enc}}"
# Input: username: admin, password: secret
# Output: Authorization: Basic YWRtaW46c2VjcmV0
urlquery
URL encode a string.
requests:
search:
method: GET
url: "/search?q={{.vars.searchTerm | urlquery}}"
# Input: searchTerm: "hello world"
# Output: /search?q=hello+world
List Functions
join
Join array elements with separator.
vars:
default:
tags: [api, test, smoke]
requests:
tagged:
method: GET
url: /api/data
headers:
X-Tags: "{{join \",\" .vars.tags}}"
# Output: X-Tags: api,test,smoke
index
Get element at index (0-based).
vars:
default:
userIds: [10, 20, 30, 40]
requests:
first-user:
method: GET
url: "/users/{{index .vars.userIds 0}}"
# Output: /users/10
Math Functions
add / sub / mul / div
Arithmetic operations.
body:
sum: "{{add .vars.count 1}}"
difference: "{{sub .vars.total .vars.used}}"
product: "{{mul .vars.price .vars.quantity}}"
quotient: "{{div .vars.total .vars.count}}"
max / min
Maximum/minimum value.
body:
maximum: "{{max .vars.limit1 .vars.limit2}}"
minimum: "{{min .vars.value1 .vars.value2}}"
Conditional Functions
default
Provide default value if empty.
body:
name: "{{.vars.userName | default \"Anonymous\"}}"
count: "{{.vars.itemCount | default \"0\"}}"
ternary
If-then-else expression.
body:
environment: "{{ternary \"prod\" \"dev\" (eq .vars.env \"production\")}}"
# If .vars.env == "production": "prod"
# Otherwise: "dev"
Crypto Functions
sha256sum / sha1sum / md5sum
Generate hash digests.
headers:
X-Content-Hash: "{{.vars.content | sha256sum}}"
X-Checksum: "{{.vars.data | md5sum}}"
htpasswd
Generate htpasswd hash.
body:
passwordHash: "{{htpasswd .vars.password}}"
Complete Examples
Authentication Flow
vars:
default:
baseUrl: https://api.example.com
username: admin
password: secret
workflows:
auth-workflow:
steps:
# Step 1: Login
- request: login
out: auth
# Step 2: Get user profile
- request: get-profile
out: profile
# Step 3: Update settings
- request: update-settings
requests:
login:
method: POST
url: "{{.vars.baseUrl}}/auth/login"
headers:
X-Request-ID: "{{uuidv4}}"
X-Timestamp: "{{now | unixEpoch}}"
body:
username: "{{.vars.username}}"
password: "{{.vars.password}}"
timestamp: "{{now | date \"2006-01-02T15:04:05Z\"}}"
get-profile:
method: GET
url: "{{.vars.baseUrl}}/users/{{.session.auth.body.userId}}"
headers:
Authorization: "Bearer {{.session.auth.body.token}}"
X-Request-ID: "{{uuidv4}}"
update-settings:
method: PUT
url: "{{.vars.baseUrl}}/users/{{.session.auth.body.userId}}/settings"
headers:
Authorization: "Bearer {{.session.auth.body.token}}"
body:
email: "{{.session.profile.body.email}}"
name: "{{.session.profile.body.name | upper}}"
updatedAt: "{{now | date \"2006-01-02T15:04:05Z\"}}"
Dynamic Request Generation
vars:
default:
baseUrl: https://api.example.com
environment: production
version: 1.2.3
requests:
dynamic-request:
method: POST
url: "{{.vars.baseUrl}}/api/v1/resources"
headers:
X-Request-ID: "{{uuidv4}}"
X-Timestamp: "{{now | date \"2006-01-02T15:04:05Z\"}}"
X-Environment: "{{.vars.environment | upper}}"
X-Version: "v{{.vars.version}}"
X-Client: "quadrastack-{{.vars.version}}"
body:
id: "{{uuidv4}}"
name: "Resource-{{now | date \"20060102-150405\"}}"
environment: "{{.vars.environment}}"
timestamp: "{{now | unixEpoch}}"
createdAt: "{{now | date \"2006-01-02T15:04:05Z\"}}"
metadata:
version: "{{.vars.version}}"
hash: "{{.vars.version | sha256sum}}"
Complex Data Transformation
vars:
default:
baseUrl: https://api.example.com
firstName: john
lastName: doe
tags: [api, production, critical]
workflows:
complex-flow:
steps:
- request: create-resource
out: created
- request: update-resource
requests:
create-resource:
method: POST
url: "{{.vars.baseUrl}}/resources"
headers:
X-Request-ID: "{{uuidv4}}"
body:
name: "{{.vars.firstName | title}} {{.vars.lastName | title}}"
email: "{{.vars.firstName | lower}}.{{.vars.lastName | lower}}@example.com"
username: "{{.vars.firstName | lower}}_{{.vars.lastName | lower}}_{{now | date \"20060102\"}}"
tags: "{{join \",\" .vars.tags}}"
hash: "{{printf \"%s-%s\" .vars.firstName .vars.lastName | sha256sum}}"
update-resource:
method: PUT
url: "{{.vars.baseUrl}}/resources/{{.session.created.body.id}}"
body:
id: "{{.session.created.body.id}}"
status: active
updatedAt: "{{now | date \"2006-01-02T15:04:05Z\"}}"
previousStatus: "{{.session.created.body.status}}"
Best Practices
1. Quote Template Expressions in YAML
Always quote template expressions to avoid YAML parsing issues:
✅ Good:
url: "{{.vars.baseUrl}}/users"
header: "Bearer {{.vars.token}}"
❌ Bad:
url: {{.vars.baseUrl}}/users # YAML parsing error
2. Use Descriptive Variable Names
vars:
default:
apiBaseUrl: https://api.example.com # Clear and descriptive
userAuthToken: abc123
requestTimeoutSeconds: 30
3. Validate Template Output
Use expect to validate template results:
requests:
test-template:
method: GET
url: "{{.vars.baseUrl}}/users/{{.vars.userId}}"
expect:
status: 200
4. Use Default Values
Provide defaults for optional variables:
body:
name: "{{.vars.userName | default \"Anonymous\"}}"
timeout: "{{.vars.timeout | default \"30s\"}}"
5. Keep Templates Readable
Break complex templates across multiple fields:
✅ Good:
body:
userId: "{{.session.auth.body.userId}}"
userName: "{{.session.auth.body.user.name}}"
userEmail: "{{.session.auth.body.user.email}}"
❌ Bad:
body:
userData: "{{.session.auth.body.userId}}-{{.session.auth.body.user.name}}-{{.session.auth.body.user.email}}"
6. Use Consistent Date Formats
Pick a standard date format and stick to it:
# ISO 8601 recommended
timestamp: "{{now | date \"2006-01-02T15:04:05Z\"}}"
7. Document Complex Templates
vars:
default:
# Base URL for API (used in all requests)
baseUrl: https://api.example.com
# User ID for test scenarios (must be valid)
userId: 123
Template Reference
Context Objects
| Context | Description | Example |
|---|---|---|
.vars.variableName | Access variable from vars profile | {{.vars.baseUrl}} |
.vars.nested.field | Access nested variable | {{.vars.api.version}} |
.session.stepName.status | HTTP status from workflow step | {{.session.auth.status}} |
.session.stepName.headers.Name | Response header from step | {{.session.auth.headers.Location}} |
.session.stepName.body.field | Response body field from step | {{.session.auth.body.token}} |
.session.stepName.bodyRaw | Raw response body from step | {{.session.auth.bodyRaw}} |
Common Functions
| Function | Description | Example |
|---|---|---|
uuidv4 | Generate UUID v4 | {{uuidv4}} |
now | Current timestamp | {{now}} |
date | Format timestamp | {{now | date "2006-01-02"}} |
unixEpoch | Unix timestamp | {{now | unixEpoch}} |
upper | Uppercase string | {{.vars.name | upper}} |
lower | Lowercase string | {{.vars.name | lower}} |
trim | Trim whitespace | {{.vars.input | trim}} |
replace | Replace substring | {{.vars.text | replace " " "_"}} |
b64enc | Base64 encode | {{.vars.data | b64enc}} |
b64dec | Base64 decode | {{.vars.encoded | b64dec}} |
sha256sum | SHA-256 hash | {{.vars.input | sha256sum}} |
join | Join array | {{join "," .vars.tags}} |
index | Array element | {{index .vars.items 0}} |
default | Default value | {{.vars.name | default "Unknown"}} |
See Sprig documentation for complete function reference.
Go Date Format Reference
Use these reference values for date formatting:
| Component | Format |
|---|---|
| Year | 2006 (4-digit), 06 (2-digit) |
| Month | 01 (numeric), Jan (short), January (long) |
| Day | 02 |
| Hour | 15 (24h), 03 (12h) |
| Minute | 04 |
| Second | 05 |
| Timezone | MST, Z07:00 |
| Day of week | Mon, Monday |
Examples:
# ISO 8601: 2024-01-15T10:30:00Z
"{{now | date \"2006-01-02T15:04:05Z\"}}"
# US: 01/15/2024
"{{now | date \"01/02/2006\"}}"
# European: 15/01/2024
"{{now | date \"02/01/2006\"}}"
# Long: Monday, January 15, 2024
"{{now | date \"Monday, January 2, 2006\"}}"
# Time only: 10:30:05
"{{now | date \"15:04:05\"}}"
See Also
- Variables - Defining variables
- Workflows - Capturing session data
- Requests - Using templates in requests
- Sprig Functions - Complete Sprig reference