Pebble Syntax in Kestra Expressions
Use this page when you need help writing expressions rather than looking up a specific filter or function.
Pebble basics
Pebble templates use two primary delimiters:
{{ ... }}to output the result of an expression{% ... %}to control template flow with tags such asif,for, orset
Examples:
{{ flow.id }}{% if inputs.region == "eu" %}Europe{% endif %}To escape Pebble syntax literally, use the raw tag described in Operators, Tags, and Tests.
Accessing values
Use dot notation for standard property access:
{{ foo.bar }}Use bracket notation for special characters or indexed access:
{{ foo['foo-bar'] }}{{ items[0] }}If a task ID, output key, or attribute contains a hyphen, use bracket notation. To avoid that, prefer camelCase or snake_case.
Parsing nested expressions
Kestra renders expressions once by default. If a variable contains Pebble that should be evaluated later, use render():
variables: trigger_or_yesterday: "{{ trigger.date ?? (execution.startDate | dateAdd(-1, 'DAYS')) }}" input_or_yesterday: "{{ inputs.mydate ?? (execution.startDate | dateAdd(-1, 'DAYS')) }}"
tasks: - id: yesterday type: io.kestra.plugin.core.log.Log message: "{{ render(vars.trigger_or_yesterday) }}"
- id: input_or_yesterday type: io.kestra.plugin.core.log.Log message: "{{ render(vars.input_or_yesterday) }}"This pattern is especially useful with namespace variables, composed flow variables, and fallback logic based on trigger context.
Multiline JSON bodies
When an HTTP request body contains multiline user input, avoid partial string interpolation. Instead, build the whole payload as a single Pebble expression so JSON escaping happens correctly.
id: multiline_input_passed_to_json_bodynamespace: company.team
inputs: - id: title type: STRING defaults: This is my title - id: message type: STRING defaults: |- This is my long multiline message. - id: priority type: INT defaults: 5
tasks: - id: hello type: io.kestra.plugin.core.http.Request uri: https://kestra.io/api/mock method: POST body: | {{ { "title": inputs.title, "message": inputs.message, "priority": inputs.priority } | toJson }}Common syntax patterns
Comments
Use Pebble comments with {# ... #}:
{# This is a comment #}{{ "Visible content" }}In YAML, continue to use # for comments outside the expression itself.
Literals and collections
Pebble supports:
- strings:
"Hello World" - numbers such as
100 + 10l * 2.5 - booleans:
true,false - null:
null - lists:
["apple", "banana"] - maps:
{"apple":"red", "banana":"yellow"}
Named arguments
Filters, functions, and macros can accept named arguments:
{{ stringDate | date(existingFormat="yyyy-MMMM-d", format="yyyy/MMMM/d") }}Macros
Macros are reusable template snippets:
{% macro input(type="text", name, value="") %} type: "{{ type }}", name: "{{ name }}", value: "{{ value }}"{% endmacro %}
{{ input(name="country") }}Macros only access their local arguments.
Control flow and fallbacks
Common patterns:
ifandelseiffor branchingforfor iteration??for fallback values? :for ternary expressions
Examples:
{{ inputs.mydate ?? (execution.startDate | dateAdd(-1, 'DAYS')) }}{% for article in articles %} {{ article.title }}{% else %} No articles available.{% endfor %}{% if category == "news" %} {{ news }}{% elseif category == "sports" %} {{ sports }}{% else %} Select a category{% endif %}For full operator and tag details, see Operators, Tags, and Tests.
When to leave this page
- Need available runtime variables: Execution Context Variables
- Need filters such as
date,jq,default, oryaml: Filter Reference - Need functions such as
render(),secret(), orprintContext(): Function Reference - Need operator details such as
??, ternary expressions, tags, or tests: Operators, Tags, and Tests
Was this page helpful?